Weighted random numbers: boundary case - java

In reference to the top answer given in this post, I've noticed that it fails for a boundary case when rnd=sum_of_weight. The fix is to generate random numbers in [0,sum_of_weight), however i was wondering why the code fails for this boundary case? Is it a flaw in the algorithm?
EDIT: Also, does the weight array need to be sorted high to low? It seems so, based on the subtraction loop.
Below is the Java code that implements the pseudo-code in the above post.
int sum_of_weight = 0;
int []choice_weight = {50, 15, 15, 10, 10}; // percentages
int num_choices = choice_weight.length;
public void init() {
for (int i = 0; i < num_choices; i++) {
sum_of_weight += choice_weight[i];
}
}
int next() {
int rnd = (int)Util.between(0, sum_of_weight);// random(sum_of_weight);
rnd=sum_of_weight; // force the exception by hitting boundary case
//System.out.print("rnd=" + rnd);
for (int i = 0; i < num_choices; i++) {
if (rnd < choice_weight[i])
return i;
rnd -= choice_weight[i];
}
throw new RuntimeException("should never get here for rnd=" + rnd);
}
public static void main(String[] args) {
SimpleWeight sw = new SimpleWeight();
sw.init();
for (int i=0; i < 10;i++) {
System.out.println(sw.next());
}
}

Step 2 of the algorithm you link to states:
2) pick a random number between 0 and less than the sum weights.
To me, this reads clearly and unambiguously that the correct way is to pick a number from [0,sum_of_weight). Picking a number from a different range (e.g. any range that includes sum_of_weight) isn't a flaw in the algorithm, it's a flaw in the implementation of that algorithm.
edit No, the weights do not need to be sorted for the algorithm to work.

For those that find it useful, here is another implementation of the above. Open to feedback too if you want to make it better. I'm still a beginner.
import java.util.Random;
public class WeightedRandom {
private int choiceWeight[];
private int numChoices = 0;
private int i = 0;
private Random r = new Random();
public WeightedRandom() {
this.choiceWeight = new int[] { 60, 35, 5 };
this.numChoices = choiceWeight.length;
}
public WeightedRandom(int[] choiceWeight) {
this.choiceWeight = choiceWeight;
this.numChoices = this.choiceWeight.length;
}
public int weightedRandomGenerator() {
int sumOfWeight = 0;
for (int i = 0; i < numChoices; i++) {
sumOfWeight += choiceWeight[i];
}
int randomNumber = r.nextInt(sumOfWeight);
for (int i = 0; i < numChoices; i++) {
if (randomNumber < choiceWeight[i])
return i;
randomNumber -= choiceWeight[i];
}
throw new RuntimeException("should never get here for RandomNumber = " + randomNumber);
}
public void printWeightedAverage(int numberOfIterations) {
int numberCount[] = new int[numChoices];
for (int n = 0; n < numberOfIterations; n++) {
i = weightedRandomGenerator();
numberCount[i]++;
}
for (int n = 0; n < numChoices; n++)
System.out.println("Occurance of " + n + " = " + (((double) numberCount[n]) / numberOfIterations) * 100 + "%");
System.out.println("--------");
}
public static void main(String[] args) {
WeightedRandom wr = new WeightedRandom();
WeightedRandom wr2 = new WeightedRandom(new int[] { 49, 25, 15, 5, 3, 2, 1 });
wr.printWeightedAverage(100_000_000);
wr2.printWeightedAverage(100_000_000);
}
}

Related

Finding largest gap between consecutive numbers in array Java

I'm currently working on a homework assignment and the final task of the assignment is to write a method to find the largest gap between consecutive numbers in an unsorted array. Example: if the array had the values {1,2,3,4,5,20} the gap would be 15. Currently the array is holding 20 values generated at random.
I'm totally lost for how I would make this happen. Initially my idea for how to solve this would be using a for loop which runs through each value of the array with another loop inside to check if the current value is equal to the previous value plus 1. If it is then store that number as the minimum in the range. Another problem I ran into was that I have no idea how to store a second number without overwriting both numbers in the range. Basically nothing i've tried is working and could really use some help or at least a nudge in the right direction.
What the method does right now is only store the value for "a" after it finds a number that isn't consecutive in the array.
Here's the code I have so far
import java.util.Arrays;
class Main {
public static void main(String[] args) {
Main m = new Main();
m.runCode();
}
public void runCode()
{
Calculator calc = new Calculator();
calc.makeList(20);
System.out.println("List:");
calc.showList();
System.out.println("Max is: " + calc.max());
System.out.println("Min is: " + calc.min());
System.out.println("Sum is: " + calc.sum());
System.out.println("Ave is: " + calc.average());
System.out.println("There are " + calc.fiftyLess() + " values in the list that are less than 50");
System.out.println("Even numbers: " + calc.Even());
}
}
class Calculator {
int list[] = new int[20];
public void makeList(int listSize)
{
for (int count = 0; count < list.length; count++) {
list[count] = (int) (Math.random() * 100);
}
}
public void showList()
{
for (int count = 0; count < list.length; count++)
{
System.out.print(list[count] + " ");
}
}
public int max()
{
int max = list[0];
for (int count=0; count<list.length; count++){
if (list[count] > max) {
max = list[count];
}
}
return max;
}
public int min()
{
int min = list[0];
for (int count=0; count<list.length; count++){
if (list[count] < min) {
min = list[count];
}
}
return min;
}
public int sum()
{
int sum = 0;
for (int count=0; count<list.length; count++){
sum = sum + list[count];
}
return sum;
}
public double average()
{
int sum = sum();
double average = sum / list.length;
return average;
}
public int fiftyLess()
{
int lessThan = 0;
for (int count =0; count<list.length;count++)
{
if (list[count] < 50)
{
lessThan++;
}
}
return lessThan;
}
public int Even()
{
int isEven = 0;
for (int count = 0; count<list.length;count++)
{
if (list[count] % 2 == 0)
{
isEven++;
}
}
return isEven;
}
public int Gap()
{
int a = 0;
int b = 0;
int gap = math.abs(a - b);
for (int count = 1; count<list.length;count++)
{
if (list[count] != list[count] + 1)
{
a =list[count];
}
}
}
}
By using the java8 stream library you could achieve this in fewer lines of code.
This code segment iterates the range of the array, and subtracts all consecutive numbers, and returns the max difference between them or -1, in case the array is empty.
import java.util.stream.IntStream;
class Main {
public static void main(String[] args) {
int[] list = {1, 2, 3, 4, 5, 20};
int max_difference =
IntStream.range(0, list.length - 1)
.map(i -> Math.abs(list[i + 1] - list[i]))
.max().orElse(-1);
System.out.println(max_difference);
}
}
Alternatively you could do this with a traditional for loop.
class Main {
public static void main(String[] args) {
int[] list = {1, 2, 3, 4, 5, 20};
int max_difference = -1;
int difference;
for (int i = 0; i < list.length - 1; i++) {
difference = Math.abs(list[i + 1] - list[i]);
if(difference > max_difference)
max_difference = difference;
}
System.out.println(max_difference);
}
}
Output for both code segments:
15

Parallel sort not finding the top numbers

I'm writing a program which is supposed to find the 25 top numbers in a large array using threads. My algorithm seems to work fine, however when comparing the result to an Arrays.sort-ed version of the original array, it seems like my top 25-list misses some of the numbers. I really hate posting this much code in a question, but I'm completely stuck on this, and has been for a couple of hours now. I'd love some help figuring out what's wrong. Here are my classes:
Main.java
import java.util.Arrays;
import java.util.Random;
public class Main {
public static void main(String[] args) {
final int NUM_THRS = 4;
int[] numbers = new int[500];
Random generator = new Random(500);
for(int i = 0; i < numbers.length; i++) {
numbers[i] = Math.abs(generator.nextInt());
}
Thread[] thrs = new Thread[NUM_THRS];
NumberThread[] nthrs = new NumberThread[NUM_THRS];
long startTime = System.currentTimeMillis();
for(int i = 0; i < thrs.length; i++) {
int start = getStart(i, thrs.length, numbers.length);
int stop = getStop(i, thrs.length, numbers.length);
nthrs[i] = new NumberThread(numbers, start, stop);
thrs[i] = new Thread(nthrs[i]);
thrs[i].start();
}
for (int i = 0; i < thrs.length; i++) {
try {
thrs[i].join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int[] top = new int[25];
int[] indices = new int[NUM_THRS];
for (int i = 0; i < indices.length; i++) {
indices[i] = 24;
}
for(int i = 0; i < top.length; i++) {
top[i] = getMax(nthrs, indices);
}
for (int i = 0; i < top.length; i++) {
System.out.println(top[i]);
}
}
public static int getMax(NumberThread[] thrs, int[] indices) {
int maxNum = 0;
int maxIdx = 0;
for(int i = 0; i < indices.length; i++) {
if(indices[i] >= 0) {
if(thrs[i].topNums[indices[i]] > maxNum) {
maxNum = thrs[i].topNums[indices[i]];
maxIdx = i;
}
}
}
System.out.println("iterate");
indices[maxIdx] = indices[maxIdx]-1;
return maxNum;
}
public static int getStart(int i, int total, int len) {
return i*len/total;
}
public static int getStop(int i, int total, int len) {
if(i != total-1) {
return (i+1)*len/total;
}
return len-1;
}
}
NumberThread.java
public class NumberThread implements Runnable {
int start, stop;
int[] numbers;
int[] topNums;
public NumberThread(int[] numbers, int start, int stop) {
this.numbers = numbers;
this.start = start;
this.stop = stop;
this.topNums = new int[25];
System.out.println(start + " " + stop);
}
#Override
public void run() {
for (int i = start; i <= stop; i++) {
inner: for (int j = topNums.length-1; j > 0; j--) {
if(numbers[i] > topNums[j]) {
topNums[j] = numbers[i];
break inner;
}
}
}
}
}
The numbers printed after main are not the same as the top numbers when I Arrays.sort the numbers-array and print the top 25. Some numbers seem to be missing.
Thanks a lot in advance.
I think that your NumberThread classes Run method isn't doing what it should do. It needs to find the 25 largest numbers in the partition you assign to it, for example if the array you are searching was already sorted then the 25 largest numbers could all be in 1 partition but what its's actually doing is overwriting the first number it finds that's smaller than the current number so you end up with less than 25 numbers and they might not be the largest.
For example consider the sequence 98 99 1 2 3... 98 would get written to topNums[19] but then overwritten with 99.
I'm also not sure about the getMax function, it seems to be trying to merge the different topNums arrays together; however the arrays aren't sorted so I don't see how it can work.

Paul Erdos Conjecture [Java]

I've been trying to solve this rather easy problem on SPOJ: http://www.spoj.com/problems/HS08PAUL/.
It requires the number of prime numbers (less than n) which can be expressed in the form x^2+y^4 (where x and y are integers) to be found out.
I've whipped up a brute force solution which takes up quite a while for (n ~= 1000000), resulting in a TLE (time limit exceeded) error being thrown by the engine. Here's the source code:
import java.io.*;
import java.util.*;
class HS08PAUL {
public static int[] sieve(int n){
boolean[] prime = new boolean[n+1];
int[] primeNumbers = new int[n];
int index = 0;
Arrays.fill(primeNumbers, 0);
Arrays.fill(prime,true);
prime[0] = false;
prime[1] = false;
int m = (int)Math.sqrt(n);
for(int i = 2; i <= m; i++){
if(prime[i])
for(int k = i*i; k<=n; k+=i)
prime[k] = false;
}
for(int j = 2; j <= n; j++) {
if(prime[j]) {
primeNumbers[index] = j;
index++;
}
}
return primeNumbers;
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
try{
double numberOfTestCases = in.nextDouble();
while(numberOfTestCases -- > 0) {
int index = 0, y = 0, count = 0;
int num = in.nextInt();
int[] primes = sieve(num);
while(index < num/3 ) {
for(y = 1; y < 57 ; y ++) {
if(Math.ceil(Math.sqrt(primes[index] - Math.pow(y,4))) == Math.floor(Math.sqrt(primes[index] - Math.pow(y,4)))) {
count++;
break;
}
}
index++;
}
System.out.println(count);
}
}
catch(Exception e) {
}
}
}
Is there a way in which I can make this approach work?
P.S.:Please ignore the unruly exception handling.
How many numbers of the form x^2+y^4 are there below 1000000? How many prime numbers are there below 1000000? What do these two numbers tell you about how you should approach the solution?
#isnot2bad's comment is also relevant.

Java Simple Lottery Program

I tried to create a simple lottery program. Here is a problem: it still prints same numbers. For example I got 33 21 8 29 21 10 as output. Everytime when random number is generated, code checks if that number is already generated, then it creates a new random number but after that it doesn't check again. I couldn't find a way to do that.
public static void main(String[] args)
{
int[] lottery = new int[6];
int randomNum;
for (int i = 0; i < 6; i++)
{
randomNum = (int) (Math.random() * 50); //Random number created here.
for (int x = 0; x < i; x++)
{
if (lottery[i] == randomNum) // Here, code checks if same random number generated before.
{
randomNum = (int) (Math.random() * 50);//If random number is same, another number generated.
}
}
lottery[i] = randomNum;
}
for (int i = 0; i < lottery.length; i++)
System.out.print(lottery[i] + " ");
}
There are 2 problems with your code:
you check if lottery[i] and randomNum are the same, it should be lottery[x]
when you re-generate a random number, you don't check it against the first numbers in lottery.
Here is a corrected version:
public static void main(String[] args) {
int[] lottery = new int[6];
int randomNum;
for (int i = 0; i < 6; i++) {
randomNum = (int) (Math.random() * 50); // Random number created here.
for (int x = 0; x < i; x++) {
if (lottery[x] == randomNum) // Here, code checks if same random number generated before.
{
randomNum = (int) (Math.random() * 50);// If random number is same, another number generated.
x = -1; // restart the loop
}
}
lottery[i] = randomNum;
}
for (int i = 0; i < lottery.length; i++)
System.out.print(lottery[i] + " ");
}
You are changing the random number while you are checking it. You need to pick one random number and check whether it is present or not.
BTW A shorter approach is to use a shuffle.
// give me all the number 1 to 50
List<Integer> list = IntStream.range(1, 51).boxed().collect(Collectors.toList());
// shuffle them.
Collections.shuffle(list);
// give me the first 6
System.out.println(list.subList(0, 6));
A simple solution, between the first (who could be very abstract for a not Java programmer) and the 2nd (not assuring the unicity of the number list).
Collection<Integer> liste = new ArrayList<Integer>();
for (int i = 0; i < 6; i++)
{
Boolean ap = false;
while (!ap)
{
Integer randomNumber = (int) (Math.random() * 50);
if (! liste.contains(randomNumber)){
liste.add(randomNumber);
ap = true;
}
}
}
for (Integer liste1 : liste) {
System.out.print(liste1+" ");
}
try this one, it creates 12 x (6 out of 45)
public static void main(String[] args) {
SecureRandom random = new SecureRandom();
for (int i = 0; i < 12; i++){
Integer[] tipp = new Integer[6];
int n = 0;
do {
int r = random.nextInt(45) + 1;
if (Arrays.asList(tipp).indexOf(r)<0){
tipp[n]= r;
n++;
}
} while (n<=5);
Arrays.sort(tipp);
System.out.println(Arrays.toString(tipp));
}
}
public static void main(String[] arg) {
int[] lottery = new int[6];
int randomNum;
c1:
for (int i = 0; i < 6; i++) {
randomNum = (int) (Math.random() * 50); // Random number created here.
if(randomNum == 0) {
continue c1;
}
for (int x = 0; x < i; x++) {
if (lottery[x] == randomNum ) // Here, code checks if same random number generated before.
{
randomNum = (int) (Math.random() * 50);// If random number is same, another number generated.
x = -1; // restart the loop
}
}
lottery[i] = randomNum;
}
for (int i = 0; i < lottery.length; i++)
System.out.print(lottery[i] + " ");
}
This is the object class for making a ticket, it will create ONE ticket with ascending values at which whatever parameters you choose. This program won't run until you have a main method that you call. Make sure to import TreeSet.
import java.util.TreeSet;
public class TicketMaker{
private int numbersPerTicket;
private int lowestNumber;
private int highestNumber;
TicketMaker(){
numbersPerTicket=0;
lowestNumber=0;
highestNumber=0;
}
TicketMaker(int numbersPerTicket,int lowestNumber,int highestNumber){
if(numbersPerTicket > 0 && lowestNumber >= 0 && highestNumber >= lowestNumber){
this.numbersPerTicket=numbersPerTicket;
this.lowestNumber=lowestNumber;
this.highestNumber=highestNumber;
}
}
public boolean printTicket(int numbersPerTicket,int lowestNumber,int highestNumber){
if(numbersPerTicket > 0 && lowestNumber >= 0 && highestNumber >= lowestNumber){
if(numbersPerTicket > highestNumber){
System.out.println("Error not in-bounds");
return false;
}
int rand;
int count=0;
System.out.println("[Ticket Printed]");
TreeSet<Integer> set = new TreeSet<>();
do{
rand = (int)(Math.random()*highestNumber)+lowestNumber;
set.add(rand);
count++;
}while(set.size() != numbersPerTicket);
System.out.println(set);
return true;
}
else{
System.out.println("Error not in-bounds");
return false;
}
}
public boolean isValidTicketData(int numbers,int lowest,int highest){
if(lowest != 1){
if(highest == numbers)
return false;
}
if(numbers <= highest){
if(numbers > 0 && lowest >= 0 && highest >= lowest)
return true;
}
return false;
}
}

Limiting duplicate random numbers

I just want to know how to limit to number of times a random number appears. I have generated random numbers of 1 to 10 and want to limit each number to appear 4 times.
myArray[i][j] = rand.nextInt(11);
for (int i=0; i < myArray.length; i++) {
for (int j=0; j < myArray[i].length; j++) {
myArray[i][j] = rand.nextInt(11);
System.out.print(" " + myArray[i][j]);
The code above creates the randoms numbers. Just want to limit them.
Since you are limited to 10 * 4 = 40 numbers you can use a list and randomize the index :
List<Integer> numbers = new ArrayList<Integer>();
for (int i = 1; i < 11; ++i) {
for (int j = 0; j < 4; ++j)
numbers.add(i);
}
And then when you assign a random number :
int i = rand.nextInt(numbers.size());
myArray[i][j] = numbers.get(i);
numbers.remove(i);
This assumes your two dimensional will not contain more then 40 numbers
My solution stores the result in arrayList:
public class Example {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
final int range = 10;
int[] numbers = new int[range + 1];
int sum = 0;
final int noOfOccurances = 4;
final int size = range * noOfOccurances;
Arrays.fill(numbers, 0);
Random generator = new Random();
List<Integer> numbersArray = new ArrayList<>();
while (sum != size) {
int randomNumber = generator.nextInt(range) + 1;
if (numbers[randomNumber] != noOfOccurances) {
numbers[randomNumber]++;
sum++;
numbersArray.add(randomNumber);
}
}
System.out.println(numbersArray);
}
}
How about storing the count of the generated int's in an array, or Map, or anything?
Map<Integer, Integer> randomCounts = new HashMap<Integer, Integer>();
... your for loops
myArray[i][j] = rand.nextInt(11);
if (randomCounts.containsKey(myArray[i][j])) {
randomCounts.put(myArray[i][j],randomCounts.get(myArray[i][j])+1);
} else {
randomCounts.put(myArray[i][j],1);
}
And if you want to check them, just iterate through your map, and voilá. :)
You can make a method to check if the generated number exists more than 4 times in the array and create a new random number if it does. It should look like this:
import java.util.Random;
public class rndNumberGenerator {
public static void main (String[] args) {
int[][] myArray = new int[2][5];
Random rand = new Random();
int randomNumber;
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 5; j++) {
do {
randomNumber = rand.nextInt(11);
} while(overMax(myArray, randomNumber) == true);
myArray[i][j] = randomNumber;
System.out.print(" " + myArray[i][j]);
}
}
}
public static boolean overMax(int[][] array, int number) {
int max = 4;
int count = 0;
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 5; j++) {
if (array[i][j] == number) {
count++;
}
}
}
if (count >= max)
return true;
else
return false;
}
}
Hope this helped you, if you have any other questions feel free to ask.
I take suggestion by pshemek (vote up): instead the ArrayList, I use the Set because it can't contain duplicate numbers and you have'nt to espicitate control.
An implementation: the copy{right, left} is of pshemek, I had only extended the idea:)
public class Example {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
int[] numbers = new int[11];
int sum = 0;
final int range = 10;
final int noOfOccurances = 4;
Arrays.fill(numbers, 0);
Random generator = new Random();
Set<Integer> numbersArray = new TreeSet<Integer>();
while (sum != range * noOfOccurances) {
int randomNumber = generator.nextInt(range) + 1;
sum++;//correction for first comment
numbersArray.add(randomNumber); // randomNumber will never be twice: a Set cointains ever one and only one instance of an determinated element
}
System.out.println(numbersArray);
}
}//end class
You could write your own:
public static class CountedRandom {
// My rng.
Random rand = new Random();
// Keeps track of the counts so far.
Map<Integer, Integer> counts = new HashMap<Integer, Integer>();
// The limit I must apply.
final int limit;
public CountedRandom(int limit) {
this.limit = limit;
}
public int nextInt(int l) {
int r;
do {
// Keep getting a new number until we hit one that has'n been overused.
r = rand.nextInt(l);
} while (count(r) >= limit);
return r;
}
private int count(int r) {
// How many times have we seen this one so far.
Integer counted = counts.get(r);
if ( counted == null ) {
// Never!
counted = new Integer(0);
}
// Remember the new value.
counts.put(r, counted + 1);
// Returns 0 first time around.
return counted;
}
}
public void test() {
CountedRandom cr = new CountedRandom(4);
for ( int i = 0; i < 50; i++ ) {
System.out.print(cr.nextInt(4)+",");
}
System.out.println();
}
Note that this will hang if you ask for too may numbers in too small a range (as I have in my test).
Prints
2,0,1,2,1,1,3,3,0,3,0,2,2,0,1,3,
and then hangs.

Categories

Resources