I have a homework to write a method that returns a random number between
1 and 54, excluding the numbers passed in the argument. The method header is
specified as follows:
public static int getRandom(int... numbers)
I can't use anything more advanced than Single-Dimensional Arrays.
my code is:
public class PE13RandomNumberChooserVer2 {
public static void main(String[] args) {
int[] excludeNumbers = {1, 2, 3, 4, 5, 6, 7, 8, 11, 12, 13, 14, 15, 16, 17, 18};
int randomNumber = getRandom(excludeNumbers);
System.out.println();
System.out.println("Random number chosen: " + randomNumber);
}
public static int getRandom(int... excludeNumbers) {
int random = 1 + (int)(Math.random() * (54 - 1) + 1);
System.out.println("Numbers to exclude: ");
for (int i = 0; i < excludeNumbers.length; i++) {
System.out.print(excludeNumbers[i] + " ");
while (excludeNumbers[i] == random) {
random = 1 + (int)(Math.random() * 54);
System.out.println("\n(for test only) next random number: " + random);
}
}
return random;
}
}
a sample run showed that my logic is wrong:
(for test only) initial random number: 8
Numbers to exclude:
1 2 3 4 5 6 7 8
(for test only) next random number: 12
11 12
(for test only) next random number: 3
13 14 15 16 17 18
Random number chosen: 3
it checks only if random is equal to the current item in the array, it doesn't consider the case in which it can be equal to the previous item in the list that is already checked.
The end result for the random generated number should be a value different from the numbers in the array.
Any suggestions how to fix it are greatly appreciated.
The following will do it:
private final Random rand = new Random();
public int getRandom(int min, int max, int... excludeNumbers) {
int random = rand.nextInt(max - min + 1 - excludeNumbers.length) + min;
for (int exc : excludeNumbers) {
if (random >= exc) {
random++;
}
}
return random;
}
Observe how it only generates a single random number and doesn't require a rejection loop.
Note that both min and max are inclusive. Also note that excludeNumbers must appear in ascending order.
This
int random = 1 + (int)(Math.random() * (54 - 1) + 1);
and this
random = 1 + (int)(Math.random() * 54);
are strange and should coincide.
Latter one is correct.
Next your loops are wrong. Loops are for regenerating number in case it coincides with prohibited one. So you should place for inside while and place println outside all loops. for should serve to check all prohibited numbers for one generated and while should server as retriement loop.
Also you can use Random class.
THE CODE
public static void main(String[] args) {
int[] excludeNumbers = {1, 2, 3, 4, 5, 6, 7, 8, 11, 12, 13, 14, 15, 16, 17, 18};
int randomNumber;
System.out.println("Numbers to exclude: ");
for (int i = 0; i < excludeNumbers.length; i++) {
System.out.print(excludeNumbers[i] + " ");
}
// 100 tests
for(int i=0; i<100; ++i ) {
randomNumber = getRandom(excludeNumbers);
System.out.println();
System.out.println("Random number chosen: " + randomNumber);
}
}
public static int getRandom(int... excludeNumbers) {
int random;
// regeneration loop
regeneration: while(true) {
// generating a number
random = 1 + (int)(Math.random() * 54);
// checking of it's correctness
for (int i = 0; i < excludeNumbers.length; i++) {
// checking if number conincides for prohibited
if( excludeNumbers[i] == random ) {
// if number concided, then going to regeneration
continue regeneration;
}
// here number has not coincided
// but with just one of prohibites
// no decision here
}
// here all prohibited numbers checked and
// no coincidences found
// so generated number is good
System.out.println("\n(for test only) next random number: " + random);
break regeneration;
}
return random;
}
Try this, which simply keeps trying until you get an acceptable number.
List<Integer> nums = Arrays.asList(excludedNumbers);
while (true) {
Random random = 1 + (int)(Math.random() * 54);
if (!nums.contains(random))
return random;
}
The method would be cleaner if you passed exclude numbers as a list.
The techniques that keep retrying until hitting an allowed number are crude and turn into a crying misery as the number of allowed numbers approaches 1. A much more elegant way is this:
create a boolean[54];
set each excluded element to true;
choose a random number r from a range as large as the number of allowed choices (54 - number of exclusions);
return the rth false element in the boolean array.
Note: this algorithm is the best fit when you can cache the boolean array; for your exact case (the function receives a new array every time) NPE's solution is superior.
Look into java.util.random, it has a method that provides a random integer between 0 and your specified number. I can't give you an example because I'm typing this from my phone at the moment, but if you were to get it between 1 and 54 I would get a random number between 0 and 53 and add 1 to the result.
I am also teaching myself Java right now. I spend several days on this problem already while there is no answer available yet online. The solution above is too advanced for me so far. Here is my solution finally, which employ basic array knowledge only
public class Q6_13 {
public static void main(String[] args) {
int[] excludeNumbers = {1, 2, 3, 4, 5, 6, 7, 8, 11, 12, 13, 14, 15, 16, 17, 18};
System.out.println (getRandom(excludeNumbers));
}
public static int getRandom(int...numbers) {
int n = (int)(Math.random() * 54) + 1; ;
boolean newRandom = false;
boolean getNew = false;
while (getNew == false) {
for (int i = 0; (i < numbers.length) && newRandom == false; i++) {
if (n == numbers[i]) {
newRandom = true;
}
}
if (newRandom) {
n = (int)(Math.random() * 54) + 1;
getNew = false;
newRandom = false;
}
else
getNew = true;
}
return n;
}
}
public static int getRandom(int... numbers) {
final Random random = new Random();
final int[] arr1 = new int[54];
int count = 54;
for(int i = 0; i < arr1.length; ++i) {
arr1[i] = i + 1; // good luck doing this with foreach
}
for(int i = 0; i < numbers.length; ++i) {
final int n = numbers[i];
if(arr1[n] != 0) {
--count;
}
arr1[n] = 0;
}
final int[] arr2 = new int[count];
for(int i = 0, j = 0; i < arr2.length; ++i, ++j) {
if(arr1[j] == 0) {
++j;
}
else {
arr2[i] = arr1[j];
}
}
return arr2[random.nextInt(count)];
}
public class Compute {
public static void main(String[] args) {
int[] values = {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41};
int x = 1+ (int) (Math.random()*54);
System.out.println("Value: "+getRandom(x,values));
}
public static int getRandom(int a, int... numbers){
System.out.println("x1: "+a);
for(int j=0;j<numbers.length;){
//System.out.println("Numbers: "+numbers[j]);
if(numbers[j]==a){
a = 1 + (int) (Math.random()*54);
System.out.println("x2: "+a);
j=0;
continue;
}
j++;
}
return a;
}
}
Related
This question already has answers here:
Creating random numbers with no duplicates
(20 answers)
Generating 10 random numbers without duplicate with fundamental techniques
(10 answers)
How to generate random numbers from 0 to 100 without duplicates when user enters the size of the array in Java?
(5 answers)
Closed last month.
I have a problem in generating the non-duplicate numbers. I tried to do the do-while loop but it seems not working. How do I fix it?
import java.util.Arrays;
public class Assignment2_Q2 {
public static void main (String[] args){
int[] myList = new int[10];
number(myList);
sortedOrder(myList);
display (myList);
}
public static void number(int[] list){
int random;
random = (int)(Math.random() * 21);
list[0] = random;
for (int i = 1; i < list.length; i++){
do{
random = (int)(Math.random() * 21);
list[i] = random;
}while(list[i] == list[i-1]);
}
}
public static void sortedOrder(int[] list){
java.util.Arrays.sort(list);
}
public static void display(int[] list){
System.out.println("The array in the sorted order:\n" + Arrays.toString(list) + "\n");
}
}
Example output:
The array in the sorted order:
[0, 0, 6, 7, 13, 16, 16, 18, 19, 20]
As you can see, 0 and 16 come twice each. They are duplicates. I have also seen a number coming three times in one run.
Generate a list of numbers 1-20, then shuffle the list, then take the first 10.
List<Integer> nums = IntStream.rangeClosed(1, 20).boxed().collect(toList());
Collections.shuffle(nums);
nums = nums.subList(0, 10);
Your problem is in while(list[i] == list[i-1]) This isn't checking that the list doesn't contain the random number you generated. It's checking that the last 2 generated numbers aren't the same You're not checking that the previous numbers didn't match. The right way to do this is:
for (int i = 0; i < list.length; i++){
bool found = false;
do{
random = (int)(Math.random() * 21);
for(int j=0; j<i; j++) {
if(list[j] == random) {
found = true;
}
}
}while(found = false);
list[i] = random;
}
public static void main(String[] args) {
int[] myList = new int[10];
number(myList);
}
public static void number(int[] list) {
int random;
Set<Integer> used = new HashSet<>();
for (int i = 0; i < list.length; i++) {
do {
random = (int) (Math.random() * 21);
} while (used.contains(random)); // check if the number has been used before
list[i] = random;
used.add(random);
}
}
After researching the Coin Change problem I tried my best to implement the solution. The code I have so far prints the minimum number of coins needed for a given sum. However, it does not print out the number of each coin denomination needed. This is what my code currently looks like:
public class Coins {
static int minCoins(int coinValues[], int m, int target) {
int[] totalCoins = new int[target + 1];
int[][] numOfCoins = new int[target + 1][m];
totalCoins[0] = 0;
for (int i = 1; i <= target; i++) {
totalCoins[i] = Integer.MAX_VALUE;
}
for (int i = 1; i <= target; i++) {
for (int j = 0; j < m; j++) {
if (coinValues[j] <= i) {
int previous = totalCoins[i - coinValues[j]];
if (previous != Integer.MAX_VALUE && previous + 1 < totalCoins[i]) {
totalCoins[i] = previous + 1;
}
}
}
}
return totalCoins[target];
}
public static void main(String args[]) {
int coinValues[] = {1, 5, 10, 20};
int m = coinValues.length;
int target = 26;
System.out.println("Minimum coins required is "+ minCoins(coinValues, m, target) );
}
}
I'm just very confused how/where I should populate numOfCoins[][].
Solution that I implemented in Groovy using Euro denominations
class coinage {
def denoms = [1, 2, 5, 10, 20, 50, 100, 200]
def resultArray = []
def remainder
def largest
def newResult
def calculate(int amt) {
def value = denoms.findAll({ element -> element <= amt})
largest = value.last()
resultArray.add(largest)
remainder = amt - largest
if (remainder == 0 || remainder == amt) {
newResult = resultArray.size()
println("Minimum number of coins required for this is: $newResult")
} else
calculate(remainder)
}
}
And to call it:
coins = new coinage()
coins.calculate(305)
#user1523236 provides the greedy method which does not solve the general case. For example, if one removes the 1 denomination and calculates the change for 8.
Please take a look at, for example, 4.12. Dynamic Programming, or The dynamic programming alogorithm for CMP (change making problem). Both references provide general dynamic programming algorithms.
I need to create random numbers that will run through an array without duplicates.
The problem is the duplication and I can't use any of the utils except the Scanner for input (teacher instruction) like java.util.Random or java.util.ArrayList.
I use a function called random that my teacher wrote to us and the function newNum(int num) is where I need what I have asked - random numbers.
package exercise;
import java.util.Scanner;
public class Bingo {
static int size = 10;
static int num;
static int[] arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
private static Scanner sc;
public static void main(String[] args) {
System.out.print("Press Enter to start: ");
sc = new Scanner(System.in);
sc.nextLine();
System.out.println("");
// int[] arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// int[] tempArray = arr;
int num = random();
// int num = sc.nextInt();
// System.out.println(num);
while (size > 0) {
System.out.println(num);
size--;
newArray(num);
num = random();
newNum(num);
// System.out.println(num);
}
}
public static int random() {
int max = 10;
double r = Math.random();
int num = (int) (r * max + 1);
return num;
}
public static int newNum(int num) {
// Here should go the code for the function for getting only new
// random number without duplications
return num;
}
public static int newArray(int num) {
int[] tempArray = arr;
arr = new int[size];
int x = num - 1;
for (int i = 0; i < x; i++) {
if (i < size) {
arr[i] = tempArray[i];
}
}
for (int i = num; i < size; i++) {
if (i < size) {
int y = i - 1;
arr[y] = tempArray[i];
} else {
int a = size - 1;
arr[a] = tempArray[size];
}
}
return num;
}
}
First of all, you write that you can't use shuffle, but that doesn't mean that you are prohibited from implementing it. It's not that hard, actually.
If you want to do that, use the Fisher-Yates shuffle, as found on wikipedia: https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
(by the way, since you are going to school, if you find such a wikipedia article - or any other article - to be interesting, you might propose to your teacher to hold an essay on that, easily earned additional good grade)
Of course, this assumes that you have a vector to shuffle, which is inefficient for large vectors ("random numbers within zero to one billion"). In this case you might want to go with:
To find n random numbers within 0..m
1. Initialize an empty list of already used random numbers which is ordered, called "numbers"
2. for i = 0..n-1
2a: r = random(0..m-i) (uniform distribution)
2b: for every entry in numbers
if entry <= r, r++
2c: sort r into numbers (maybe by using a single bubblesort step)
This shifts the complexity from the size of the vector as before to the amount of generated numbers.
Explanation: In every iteration, we want to find an unused number. We find the rth unused number (there is a range of 0..m-i of unused numbers in iteration i). Now we only need to find out which number is the rth unused one. This is done by the inner iteration. We need numbers to be sorted because of this example: current state: numbers = {5, 1}, r = 4. r < 5 -> do nothing. r >= 1 -> r++. End up with r = 5, got a double entry.
If sorting is not wanted for the resulting list, simply go with two lists.
I want to make a program that will generate a random number. I know I need to use Math.random() for this but I want to set a ratio for a number that can repeat. for example, I want the compiler to give a number from 1-10 but I want 5 repeat 3 times more than another number. how do I do that? please help thank you.
for exactly that case:
private static final int[] CHOICES = new int[] { 1, 2, 3, 4, 5, 5, 5, 6, 7, 8, 9, 10 };
public static int strangeRandom() {
return CHOICES[ThreadLocalRandom.current().nextInt(CHOICES.length)];
}
selects randomly from a given set of choices & you control the choices so that 5 is more likely.
First, you should use the Random class, not Math.random(). For one, it has the nice helper method nextInt(int n) that generates a random integer between 0 and n-1 (inclusive).
In your particular case, you want a number 1-10, so generate 0-9 and add 1, i.e. nextInt(10) + 1.
But you want the number 5 to occur 3 times more often. A quick dirty way is the generate two extra number (1-12) and map them to 5.
Random rnd = new Random();
int num = rnd.nextInt(12) + 1;
if (num == 11 || num == 12)
num = 5;
As I said, quick and dirty, but it does the trick for your particular case.
Now, for a more generic solution, you want to be able to specify weighted probabilities. Numbers 1-4, 6-10 has a weight of 1, and 5 has a weight of 3.
What you then do is sum the weight (12), and generate a random number, then find the number where the accumulated weight exceeds the random number.
Here is a method for that:
private static int random(Random rnd, int ... weights) {
if (weights.length < 2)
throw new IllegalArgumentException("Need at least two weights");
int total = 0;
for (int weight : weights) {
if (weight <= 0)
throw new IllegalArgumentException("Invalid weight: " + weight);
if ((total += weight) < 0)
throw new IllegalArgumentException("Weight overflow");
}
for (int i = 0, val = rnd.nextInt(total); ; i++, val -= weights[i])
if (val < weights[i])
return i;
}
You could then call it like this:
Random rnd = new Random();
int num = random(rnd, 1,1,1,1,3,1,1,1,1,1) + 1;
Notice the +1, since the method is like Random.nextInt(n) and returns a number 0 to n-1, where n is the number of weights given.
You could easily turn it into a nice class, where the weights are given on the constructor, and the class manages the Random object for you.
Beware that performance will degrade as number of weights go up. There is a way to use TreeMap or binarySearch to improve that, but you need the class implementation, so it can prepare the data.
This will have the 5 come up at least 3 times as often as the other 9 numbers:
private static int getRandom ()
{
Random r = new Random();
int next = r.nextInt(100000) + 1;
if (next <= 25000)
{
return 5;
}
else
{
return r.nextInt(10) + 1;
}
}
Note: You can also preclude the 5 in the else case to try and hit the "3 times" likelihood better. I didn't for simplicity.
Another Variation:
Here is a variation that uses modulus of the nextInt() instead of 25000/100000 split.
Also, I put code in the else case that tries to preclude the 5 (without the infinite loop).
private static int getRandom ()
{
Random r = new Random();
int next = r.nextInt();
if (next % 4 == 0)
{
return 5;
}
else
{
int p = r.nextInt(10) + 1;
int tries = 0;
// try to not get a 5 for a few times
while (p == 5 && tries++ < 4)
{
p = r.nextInt(10) + 1;
}
return p;
}
}
Test Code:
public static void main(String[] args)
{
Map<Integer, Integer> frequecnyMap = new HashMap<Integer, Integer>();
for (int i = 0; i < 12000; i++)
{
int r = getRandom();
Integer n = frequecnyMap.get(r);
if (n == null)
{
frequecnyMap.put(r, 1);
}
else
{
frequecnyMap.put(r, n + 1);
}
}
System.out.println(frequecnyMap);
}
Sample Output (2nd Variation):
{1=971, 2=975, 3=995, 4=1037, 5=3025, 6=1042, 7=995, 8=976, 9=969, 10=1015}
{1=1016, 2=1019, 3=994, 4=968, 5=3068, 6=1030, 7=996, 8=914,
9=990, 10=1005}
{1=939, 2=944, 3=979, 4=986, 5=3023, 6=1040, 7=1007, 8=1046, 9=997, 10=1039}
If you want to have full control of your random generated numbers, you should do something like this:
public class MyRandom {
private int[] probability;
private long[] ntimes;
private long times;
public MyRandom(int[] probability) {
this.probability = new int[10];
System.arraycopy(probability, 0, this.probability, 0, probability.length);
ntimes=new long[10];
for(int i=0; i < ntimes.length; i++)
ntimes[i]=0;
times=0;
}
public void showProbability() {
for (long i : probability) {
System.out.print(i+" ");
}
System.out.println();
}
public int random() {
int t = 10;
int r = (int)Math.floor(Math.random()*10+1);
double p = 0;
if (times == 0)
p = 0;
else
p = ntimes[r-1]*100/times;
System.out.println("P: "+p +" : "+probability[r-1]);
while (p > probability[r-1] && t > 0) {
r = (int)Math.floor(Math.random()*10+1);
p = ntimes[r-1]*100/times;
t--;
}
ntimes[r-1]++;
times++;
return r;
}
public long getOcurrences(int i) {
return ntimes[i-1];
}
//This is an example of how to use it.
public static void main(String[] args) {
int[] p = {5, 5, 5, 5, 30, 5, 5, 5, 10, 15};
MyRandom mr = new MyRandom(p);
for (int i = 0; i < 2000; i++) {
int r = mr.random();
System.out.println("Id: "+i+" Number: "+r+" Ocurrences: "+mr.getOcurrences(r));
}
}
}
Set up to print out all false values which are prime numbers however out of 25 it prints. 3, 5, 7, 8, 9, 11, 13, 14, 15, 17, 19, 20, 21, 23, 24, not sure why some of them slip by. Any insight into the matter would be nice.
Or simply pointing me in the write direction.
Why are the non-prime numbers such as 8 being printed?
import java.util.Arrays;
import java.util.Scanner;
class Sieve {
public static void main(String args[]) {
Scanner inputScanner;
inputScanner = new Scanner(System.in);
//determine max value
System.out.println("I will determine all the primality of a set of numbers, enter the max");
int n = Integer.parseInt (inputScanner.nextLine());
boolean[] truedBooleanArray = calcBooleanMax (n);
//call upon function to check primality
boolean [] primeNumbers = calcPrimality (truedBooleanArray);
// call upon function to print out prime numbers
printPrimes(primeNumbers);
}
public static boolean[] calcBooleanMax(int maxNumber) {
boolean [] maxNumberArray = new boolean [maxNumber];
maxNumberArray[0] = false;
maxNumberArray[1] = false;
//asigns 1, 0 to false
//change all boleans within array from false to true!
for(int i=1; i < maxNumber; i++) {
maxNumberArray [i] = true;
}
return maxNumberArray;
}
public static boolean[] calcPrimality(boolean [] truedBooleans) {
for(int i = 2; i <=truedBooleans.length; i++) {
//check every number greater than 1 for primality.
if (truedBooleans[i-1]) {
}
//finds multiples and makes sure they arent stored
for(int j = 2*i; j <= truedBooleans.length; j+= i) {
truedBooleans[j-1] = false;
}
}
return truedBooleans;
}
public static void printPrimes(boolean [] thePrimeNumbers){
System.out.println("The prime numbers are [");
for(int i = 2; i<thePrimeNumbers.length; i++) {
if(thePrimeNumbers[i] == false ) {
System.out.print(i + ", ");
}
}
}
}
You have a few errors.
The array must be one larger than the given max
You are accidentally adding one back to the sieve when initializing
When removing multiples from the sieve, you must first make sure the initial number "i" is still in the sieve
You want to print the items that are still in the sieve, so print when true rather than false
Here is the fixed code
public static boolean[] calcBooleanMax(int maxNumber) {
boolean [] maxNumberArray = new boolean [maxNumber+1];
maxNumberArray[0] = false;
maxNumberArray[1] = false;
//asigns 1, 0 to false
//change all boleans within array from false to true!
for(int i=2;i < maxNumber+1; i++) {
maxNumberArray [i] = true;
}
return maxNumberArray;
}
public static boolean[] calcPrimality(boolean [] truedBooleans){
for(int i = 2; i <truedBooleans.length; i++) {
if(truedBooleans[i]) {
//finds multiples and makes sure they arent stored
for(int j = 2*i; j < truedBooleans.length; j+= i) {
truedBooleans[j] = false;
}
}
}
return truedBooleans;
}
public static void printPrimes(boolean [] thePrimeNumbers){
System.out.println("The prime numbers are [");
for(int i = 2;i<thePrimeNumbers.length;i++) {
if(thePrimeNumbers[i] ) {
System.out.print(i + ", ");
}
}
}
A simpler solution is a less literal interpretation of the algorithm. Rather than keeping a literal list of booleans, you can keep a list of the current primes. This makes the code simpler and easier to read.
Here is an example of a solution (that relies on Java 8 streams):
class Sieve {
private long current = 2;
private final List<Long> primes = new ArrayList<>();
public long nextPrime() {
while (primes.stream().anyMatch(p -> current % p == 0))
current++;
primes.add(current);
return current;
}
}