How to build a random integer array of distinct elements? [duplicate] - java

This question already has answers here:
Generating Unique Random Numbers in Java
(21 answers)
Closed 1 year ago.
I am trying to create a method that fills an array with random integers with no duplicate elements. I'm having trouble making sure each element that is put into the new array is distinct.
Ex. if numOfDigits is 5, then I'd like something like [3][8][2][6][1]. At the moment it either outputs something like [9][0][1][0][0] or infinitely loops.
private static int[] hiddenSet(int numOfDigits){
int[] numArray = new int[numOfDigits];
int temp;
for (int i = 0; i < numArray.length; i++){
do {
temp = getRandomNum(10);
numArray[i] = temp;
} while (isDigitNew(numArray, temp));
//Each random num must be unique to the array
}
return numArray;
}
private static boolean isDigitNew(int[] numArray, int index){
for (int i = 0; i < numArray.length; i++) {
if (numArray[i] == index) {
return false;
}
}
return true;
}

One easy approach is to fill the array with distinct digits then shuffle it.
public static int[] getRandomDistinct(int length) {
Random rand = new Random();
int[] array = new int[length];
// Fill with distinct digits
for (int i = 0; i < length; i++) {
array[i] = i;
}
// Swap every element with a random index
for (int i = 0; i < length - 1; i++) {
int swapWith = i + rand.nextInt(length - i);
int tmp = array[i];
array[i] = array[swapWith];
array[swapWith] = tmp;
}
return array;
}

Your algorithm takes quadratic time at best. When the choice of random numbers becomes less looping may take ages. Even infinite might be possible.
Add a positive random number + 1 to the previous generated number. The desired range of numbers needs a bit of care.
At he end shuffle. If you start with a List, you can use Collections. shuffle.

You can use IntStream like this.
private static int[] hiddenSet(int numOfDigits) {
return IntStream.iterate(getRandomNum(10), i -> getRandomNum(10))
.distinct()
.limit(numOfDigits)
.toArray();
}
and
public static void main(String[] args) {
int[] a = hiddenSet(5);
System.out.println(Arrays.toString(a));
}
output:
[7, 4, 5, 0, 1]

Related

How to create a list of random numbers with no duplicates?

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.

Removing specific value from array (java)

i have integer a = 4 and array b 7,8,9,4,3,4,4,2,1
i have to write a method that removes int ALL a from array b
desired result 7,8,9,3,2,1
This is what I have so far,
public static int[] removeTwo (int x, int[] array3)
{
int counter = 0;
boolean[] barray = new boolean [array3.length];
for (int k=0; k<array3.length; k++)
{
barray[k] = (x == array3[k]);
counter++;
}
int[] array4 = new int [array3.length - counter];
int num = 0;
for (int j=0; j<array3.length; j++)
{
if(barray[j] == false)
{
array4[num] = array3[j];
num++;
}
}
return array4;
I get this error
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
at Utility.removeTwo(Utility.java:50)
at Utility.main(Utility.java:18)
Java Result: 1
Any help would be much appreciated!
The error stems from this for loop:
for (int k=0; k<array3.length; k++)
{
barray[k] = (x == array3[k]);
counter++;
}
when you create int[] array4 = new int [array3.length - counter]; you are creating an array with size 0. You should only increment the counter if the item is the desired item to remove:
for (int k=0; k<array3.length; k++)
{
boolean b = (x == array3[k]);
barray[k] = b;
if(b) {
counter++;
}
}
To answer your question in the comment, the method should be called and can be checked like this:
public static void main(String[] args) {
int[] array3 = {0,1,3,2,3,0,3,1};
int x = 3;
int[] result = removeTwo(x, array3);
for (int n : result) {
System.out.print(""+ n + " ");
}
}
On this line:
int[] array4 = new int [array3.length - counter];
You create an array with size 0, as counter is equal to array3.length at this point.
This means that you cannot access any index in that array.
You are creating
int[] array4 = new int [array3.length - counter];// 0 length array.
you can't have 0th index there. At least length should 1 to have 0th index.
BTW my suggestion, it is better to use List. Then you can do this easy.
Really an Array is the wrong tool for the job, since quite apart from anything else you will end up with stray values at the end that you cannot remove. Just use an ArrayList and that provides a removeAll() method to do what you need. If you really need arrays you can even do:
List<Integer> list = new ArrayList(Arrays.asList(array))
list.removeAll(4);
array = list.toArray();
(Exact method names/parameters may need tweaking as that is all from memory).
the simplest way is to work with a second array where you put in the correct values
something likte that
public static int[] removeTwo (int x, int[] array3)
{
int counter = 0;
int[] array4 = new int[array3.lenght];
for (int i = 0; i < array3.lenght; i ++) {
if(array3[i] == x){
array4[counter] = array3[i];
}
}
return array4;
}
anoterh way is to remove the x calue from the array3 and shift the values behind forward
The best way to remove element from array is to use List with Iterator. Try,
Integer[] array = {7, 8, 9, 4, 3, 4, 4, 2, 1};
List<Integer> list = new ArrayList(Arrays.asList(array));
for(Iterator<Integer> it=list.iterator();it.hasNext();){
if(it.next()==4){
it.remove();
}
}

How to check ensure a array does not contain the same element twice

I have a loop which assigns randomly generated integers into an array.
I need a way to ensure the same integer is not input into the array twice.
I figured creating a loop inside the overall loop would work but I am unsure on what to execute here.
int wwe[] = new int[9];
for(int i = 0; i < 9 ; i++){
int randomIndex = generator.nextInt(wwe.length);
wwe[i] = randomIndex;
System.out.println(wwe[i]);
System.out.println("########");
for(int j = 0; j < 9; j++){
System.out.println("This is the inner element " + wwe[j]);
}
}
If you want to enforce unique values, use a data structure meant for such a behavior, like a Set. TreeSet or HashSet would work perfectly.
You are actually looking for shuffling your array.
Note that what you really looking for is to find a random order of your array, this is called a permutation.
In java, it can be simply done using a list with Collections.shuffle().
If you are looking to implement it on your own - use fisher yates shuffle, it is fairly easy to implement.
Since other answers showed how to do it with Collections.shuffle() already - here is a simple implementation + example of fisher yates shuffle, that does not need to convert the original array into a list.
private static void swap (int[] arr, int i1, int i2) {
int temp = arr[i1];
arr[i1] = arr[i2];
arr[i2] = temp;
}
private static void shuffle(int[] arr, Random r) {
for (int i =0; i < arr.length; i++) {
int x = r.nextInt(arr.length - i) + i;
swap(arr,i,x);
}
}
public static void main(String... args) throws Exception {
int[] arr = new int[] {1 , 5, 6, 3, 0, 11,2,9 };
shuffle(arr, new Random());
System.out.println(Arrays.toString(arr));
}
Something similar to the following should meet your requirement.
It uses a HashSet to achieve unique elements.
Set<Integer> sint = new HashSet<>();
Random random = new Random();
while ( sint.size() < 9){
sint.add(random.nextInt());
}
For you example, you can use Collections.shuffle
public static void main(String[] args) {
List<Integer> a = new ArrayList<>(9);
for (int i = 0; i < 9; i++) {
a.add(i);
}
Collections.shuffle(a);
System.out.println(a);
}

Arrays.sort not filling array, overwriting values that are already in the array

I need to generate an array int[] randomNumbers of random numbers with no duplicates. To do this, I make an array with all values that can go into randomNumbers, then use a random number generator to pick one out of the list, check if it's already in randomNumbers, and if it isn't, put it in randomNumbers.
(I want numbers between 1 and max, not 0 and max-1)
To be able to use Arrays.sort(int[]), the list needs to be sorted. So I use a third array, with the same values as randomNumbers called sortedNumbers, and sort it on every iteration:
public int[] uniqueRandom(int max, int numRequired) {
if (max < numRequired) {
numRequired = max;
}
int[] randomNumbers = new int[numRequired];
int[] sortedNumbers = new int[numRequired];
int[] sequentialNumbers = new int[max];
for (int i = 1; i < max; i++) {
sequentialNumbers[i] = i;
System.out.println(sequentialNumbers[i]);
}
int p = 0;
while (p < numRequired) {
int j = r.nextInt(max) + 1;
System.out.println("J:" + j);
if (Arrays.binarySearch(sortedNumbers, j) >= 0) {
System.out.println("Number Found:" + Arrays.binarySearch(randomNumbers, j));
} else {
randomNumbers[p] = j;
sortedNumbers[p] = j;
Arrays.sort(sortedNumbers);
for (int i = 0; i < randomNumbers.length; i++) {
System.out.println("rNum[" + i + "]:" + randomNumbers[i]);
}
System.out.println("\n");
for (int i = 0; i < randomNumbers.length; i++) {
System.out.println("sNum[" + i + "]:" + sortedNumbers[i]);
}
p++;
}
}
return randomNumbers;
}
My issue is that I'm getting an output where sortedNumbers is overwriting values. For uniqueRandom(5, 5) the output is:
J:2
rNum[0]:2
rNum[1]:0
rNum[2]:0
rNum[3]:0
rNum[4]:0
sNum[0]:0
sNum[1]:0
sNum[2]:0
sNum[3]:0
sNum[4]:2
J:2 // 2 already in the list, try again
J:2
J:4
rNum[0]:2
rNum[1]:4
rNum[2]:0
rNum[3]:0
rNum[4]:0
sNum[0]:0
sNum[1]:0
sNum[2]:0
sNum[3]:2
sNum[4]:4
J:5
rNum[0]:2
rNum[1]:4
rNum[2]:5
rNum[3]:0
rNum[4]:0
sNum[0]:0
sNum[1]:0
sNum[2]:2
sNum[3]:4
sNum[4]:5
J:2
J:3
rNum[0]:2
rNum[1]:4
rNum[2]:5
rNum[3]:3
rNum[4]:0
sNum[0]:0 // Should be:
sNum[1]:0 // 2
sNum[2]:2 // 3
sNum[3]:3 // 4
sNum[4]:5 // 5
J:4
rNum[0]:2
rNum[1]:4
rNum[2]:5
rNum[3]:3
rNum[4]:4
sNum[0]:0
sNum[1]:0
sNum[2]:2
sNum[3]:3
sNum[4]:4
So you can see the issue. I'm using java 1.7, and have no idea why it's doing this!
To solve your problem I would use a Set, that assure us to have unique results.
Below snipest will generate array with required number of unique integers.
Set<Integer> uniqueNumbers = new HashSet<Integer>();
Random r = new Random();
while(uniqueNumbers.size() < numRequired) {
uniqueNumbers.add(r.nextInt(maxRandom) + 1);
}
return uniqueNumbers.toArray(new Integer[0]);
You are putting the new number into both arrays using the same index. Your rNum array is filling from top down, but the sorted array is not: Each time you sort it, the new value moves down in the array and the zeros are always at the top. I think you could fix it by always putting the new number in the first position of the sorted array:
sortedNumbers[0] = j;
When you input J=5
the sortedNUm[] is
sNum[0]:0
sNum[1]:0
sNum[2]:2
sNum[3]:4
sNum[4]:5
next when you input J=3 (your p=3)
after
sortedNumbers[p] = j;
your sNUM[3] which is 4 is replaced by 3
hence after sorting it becomes
sNum[0]:0 // Should be:
sNum[1]:0 // 2
sNum[2]:2 // 3
sNum[3]:3 // 4
sNum[4]:5 // 5
notice 4 is not present
I suggest you initialize the array to -1 or 0 and add the variables at the start of array
like
sortedNumbers[0]=j;
and after Arrays.sort(); the first position will always be empty to add more numbers
While it doesn't answer the question, here is an alternative which is O(n) and work well provided max is not large.
public static void main(String[] args) {
System.out.println(Arrays.toString(uniqueRandom(20, 10)));
}
public static int[] uniqueRandom(int max, int numRequired) {
int[] possible = new int[max];
int[] ret = new int[numRequired];
for (int i = 0; i < max; i++)
possible[i] = i + 1;
Random r = new Random();
int numLeft = max;
for (int i = 0; i < numRequired; i++) {
int idx = r.nextInt(numLeft);
ret[i] = possible[idx];
if (idx < --numLeft)
possible[idx] = possible[numLeft];
}
return ret;
}
prints
[4, 10, 12, 19, 8, 3, 15, 1, 14, 7]
What I am trying to say is that perhaps you could make it simpler.
There are a few issues with your code:
since you only increment p when the new number j doesn't already exist in the arrays, that, combined with the fact that you sort the sortedArray first leads to the value actually being placed sometimes over an existing value (which shifted position due to the sort)
I don't understand what's the use of the sequentialNumbers array...
Here's an example which should work:
private static Random r = new Random();
public static void main(String[] args) {
System.out.println(Arrays.toString(uniqueRandom(10, 10)));
}
public static int[] uniqueRandom(int max, int numRequired) {
if (max < numRequired) {
numRequired = max;
}
int[] randomNumbers = new int[numRequired];
int[] sortedNumbers = new int[numRequired];
Arrays.sort(sortedNumbers);
int p = 0;
while (p < numRequired) {
int j = r.nextInt(max) + 1;
if(Arrays.binarySearch(sortedNumbers, j)<0) {
randomNumbers[p] = j;
System.arraycopy(randomNumbers, 0, sortedNumbers, 0, randomNumbers.length);
Arrays.sort(sortedNumbers);
p++;
}
}
return randomNumbers;
}

Eliminating Recursion

I've just been looking at the following piece of code
package test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Main {
public static void main(final String[] args) {
final int sizeA = 3;
final int sizeB = 5;
final List<int[]> combos = getAllCombinations(sizeA-1, sizeB);
int counter = 1;
for(final int[] combo : combos) {
System.out.println("Combination " + counter);
System.out.println("--------------");
for(final int value : combo) {
System.out.print(value + " ");
}
System.out.println();
System.out.println();
++counter;
}
}
private static List<int[]> getAllCombinations(final int maxIndex, final int size) {
if(maxIndex >= size)
throw new IllegalArgumentException("The maximum index must be smaller than the array size.");
final List<int[]> result = new ArrayList<int[]>();
if(maxIndex == 0) {
final int[] array = new int[size];
Arrays.fill(array, maxIndex);
result.add(array);
return result;
}
//We'll create one array for every time the maxIndex can occur while allowing
//every other index to appear, then create every variation on that array
//by having every possible head generated recursively
for(int i = 1; i < size - maxIndex + 1; ++i) {
//Generating every possible head for the array
final List<int[]> heads = getAllCombinations(maxIndex - 1, size - i);
//Combining every head with the tail
for(final int[] head : heads) {
final int[] array = new int[size];
System.arraycopy(head, 0, array, 0, head.length);
//Filling the tail of the array with i maxIndex values
for(int j = 1; j <= i; ++j)
array[size - j] = maxIndex;
result.add(array);
}
}
return result;
}
}
I'm wondering, how do I eliminate recursion from this, so that it returns a single random combination, rather than a list of all possible combinations?
Thanks
If I understand your code correctly your task is as follows: give a random combination of numbers '0' .. 'sizeA-1' of length sizeB where
the combination is sorted
each number occurs at least once
i.e. in your example e.g. [0,0,1,2,2].
If you want to have a single combination only I'd suggest another algorithm (pseudo-code):
Randomly choose the step-up positions (e.g. for sequence [0,0,1,1,2] it would be steps (1->2) & (3->4)) - we need sizeA-1 steps randomly chosen at sizeB-1 positions.
Calculate your target combination out of this vector
A quick-and-dirty implementation in java looks like follows
// Generate list 0,1,2,...,sizeB-2 of possible step-positions
List<Integer> steps = new ArrayList<Integer>();
for (int h = 0; h < sizeB-1; h++) {
steps.add(h);
}
// Randomly choose sizeA-1 elements
Collections.shuffle(steps);
steps = steps.subList(0, sizeA - 1);
Collections.sort(steps);
// Build result array
int[] result = new int[sizeB];
for (int h = 0, o = 0; h < sizeB; h++) {
result[h] = o;
if (o < steps.size() && steps.get(o) == h) {
o++;
}
}
Note: this can be optimized further - the first step generates a random permutation and later strips this down to desired size. Therefore it is just for demonstration purpose that the algorithm itself works as desired.
This appears to be homework. Without giving you code, here's an idea. Call getAllCombinations, store the result in a List, and return a value from a random index in that list. As Howard pointed out in his comment to your question, eliminating recursion, and returning a random combination are separate tasks.

Categories