Parallel Programming Assignment in java - java

I am busy on a parallel programming assignment, and I am really stuck. To be honest I am not entirely sure how each method works, but I think I have an idea.
I need to sum an array of consecutive values (in parallel). Seems easy enough, but I get 0 as an answer every time I try. I really don't know why.
class SumThreaded extends RecursiveTask<Integer> {
static int SEQUENTIAL_THRESHOLD = 10000;
double lo=0.0;
double hi=0.0;
long[] arr;
public SumThreaded(long[] array, double a, double b) {
arr=array;
lo=a;
hi=b;
}
public Integer compute() {
//System.out.println(mid);
if(hi - lo <= SEQUENTIAL_THRESHOLD) {
int ans = 0;
for(int i= (int) lo; i < hi; ++i)
ans += arr[i];
return ans;
}
else {
SumThreaded left = new SumThreaded(arr,lo,(hi+lo)/2.0);
SumThreaded right = new SumThreaded(arr,(hi+lo)/2.0,hi);
left.fork();
int rightAns = right.compute();
int leftAns = left.join();
return leftAns+rightAns;
}
}
public static void main(String args[]){
int size = 1000000;
long [] testArray=new long[size];
for(int i=0;i<size;i++){
testArray[i]=i+1;
}
SumThreaded t = new SumThreaded(testArray,0.0,testArray.length);
ForkJoinPool fjPool = new ForkJoinPool();
int result =fjPool.invoke(t);
System.out.println(result);
}
}
Any help would be greatly appreciated.

Your problem appears to be that you have two separate constructors for SumThreaded, only one of which sets the class's fields. When you're feeding in the long[] array from the new in sumArray, you throw the array away. You need to pick whether you're using ints or longs (and the sum of a big array is likely to need a long) and then make sure your values are getting set appropriately. Debugging and setting a breakpoint on compute would have shown you this.

Related

Are arrays supposed to be faster than ArrayLists by this much?

I have implemented two methods, shuffleList and shuffleArray which use the exact same functionality. And I have an ArrayList of half millions integer, and an array of the same half million ints. In my benchmarking code, which performs each one of the methods a 100 times on the corresponding array or ArrayList and records the time, it looks like shuffleArray takes around 0.5 seconds while shuffleList takes around 3.5 seconds, even though the code does not use any ArrayList methods but get and set, which are supposed to work as fast as they work in an array.
Now I know that ArrayLists are a little bit slower because they internally use arrays but with some additional code, but does it make this big of a difference?
void shuffleList(List<Integer> list){
Random rnd = ThreadLocalRandom.current();
for(int i=list.size()-1;i>0;i--){
int index=rnd.nextInt(i+1);
int a=list.get(index);
list.set(index,list.get(i));
list.set(i,a);
}
}
void shuffleArray(int[] ar)
{
Random rnd = ThreadLocalRandom.current();
for (int i = ar.length - 1; i > 0; i--)
{
int index = rnd.nextInt(i + 1);
int a = ar[index];
ar[index] = ar[i];
ar[i] = a;
}
}
Benchmarking code:
import org.openjdk.jmh.Main;
import org.openjdk.jmh.annotations.*;
#BenchmarkMode(Mode.AverageTime)
public class MyBenchmark {
#Benchmark
#Fork(value = 1)
#Warmup(iterations = 3)
#Measurement(iterations = 10)
public void compete() {
try {
Sorting sorting = new Sorting();
sorting.load();
System.out.println(sorting.test());
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
Main.main(args);
}
}
protected List<Integer> list = new ArrayList<Integer>();
protected List<int[]> arrays= new ArrayList<>();
protected void load(){
try (Stream<String> stream = Files.lines(Paths.get("numbers.txt"))) {
stream.forEach(x -> list.add(Integer.parseInt(x)));
} catch (IOException e) {
e.printStackTrace();
}
finally{
int[] arr =new int[list.size()];
for(int i=0;i<list.size();i++)
arr[i]=list.get(i);
arrays.add(arr);
}
}
protected double test(){
int arr[]=arrays.get(0);
Stopwatch watch = new Stopwatch();
for (int i=0; i<100; i++){
shuffleArray(arr);
shuffleList(list);
}
return watch.elapsedTime();
}
I comment out one of the methods on the for loop and use the other.
Update:
I did what a lot of you suggested of changing Int a to Integer a in the shuffleList method, and it is making it a little bit faster, it is 3 seconds instead of 3.5 now, but I still think it is a big difference.
It is worth mentioning that changing int[] arr to Integer[] arr in the shuffleArray method with keeping int a as it is to simulate the boxing and unboxing time for the array does actually make it a lot slower, it makes it take around 3 seconds, so I can make the array as slow as the ArrayList but I can not do the opposite.
Update:
Using Collections.swap() in shuffleList did indeed make it as fast as the array, but I still do not understand why, is my benchmarking too sensetive or does it really matter?
Final shuffleList code, courtesy of Andy Turner and Joop Eggen:
protected void shuffleList(List<Integer> list){
Random rnd = ThreadLocalRandom.current();
for(int i=list.size()-1;i>0;i--){
int index=rnd.nextInt(i+1);
Collections.swap(list, i, index);
}
}
Use Integer a, which save one unboxing and one boxing operation.
for (int i = list.size()-1; i>0; i--){
int index=rnd.nextInt(i+1);
Integer a=list.get(index);
list.set(index,list.get(i));
list.set(i,a);
}
And the Integer objects use more memory.
#Andy Turner mentioned the exist Collections#swap.
for (int i = list.size()-1; i > 0; i--) {
int index = rnd.nextInt(i+1);
Collections.swap(list, i, index);
}
Without warm-up of JIT compiler this might slow-down the bench-mark,
but will look better in production code. Though then you would probably use the Collections.shuffle anyway.
As commented the swap version is fast too. First the OP showed using the right microbenchmarking code.
swap uses the original Integer class too. It does l.set(i, l.set(j, l.get(i))); in order to swap - as set returns the previous element at that position. The JIT compiler can probably unwrap set and utilize that previous element immediately.
There is a Java function to do the job:
Collections.shuffle( list );
This should be significantly faster than a for loop.

Setting up array of complex coefficients, avoiding the leading zero's

I have created a class for complex numbers:
public class Complex {
private double x; //Real part x of the complex number x+iy.
private double y; //Imaginary part y of the complex number x+iy.
public Complex(double x, double y) { //Constructor: Initializes x, y.
this.x=x;
this.y=y;
}
public Complex(double x) { //Real constructor - initialises with a real number.
this(x, 0.0);
}
public Complex() { //Default constructor; initialiase x and y to zero.
this(0.0, 0.0);
}
}
What I would like to do is create a function Polynomial, which would take an array of coefficients, and filter it so that if for example [1,0,0,1,0,0,0,0,0...], it would return an array of length 4. Since the zero's that are left, have no use in a polynomial.
Here's how a complex array would look like
Complex [] coeff = new Complex [] {
new Complex(-1.0 ,0.0), new Complex(),
new Complex() , new Complex(1.0, 0.0)
};
A polynomial would be defined as
Polynomial p = new Polynomial(coeff);
Here's the problem formulation:
Here is how the polynomial would have to look like, typing in the complex array coefficients
I was thinking of constructing an algorithm which searches for the first zero of the zero sequence(which is until the end of the array), and then deletes the zeros.
Also I was thinking of inverting the entries of the array so that [0,1,1,0,1,0,0,0] would be [0,0,0,1,0,1,1,0] and then creating a function which would start "recording" my new array from the first non Trivial entry.
How would I go with creating such a function?
My attempt for this is:
int j=0;
for(int i=coeff.length-1; i>=0; i-=1)
{
if(coeff[i].getReal()== 0 && coeff[i].getImag() == 0 ){
j=+1;
}
else {
break;
}
}
int a = coeff.length-j;
this.coeff = new Complex[a];
for (int i=0;i<this.coeff.length;i+=1){
this.coeff[i]=coeff[i];
}
}
And for example I would like to print :
Complex a1=new Complex(-3, 1);
Complex a2=new Complex(2, 0.3);
Complex a3=new Complex();
Complex b=new Complex();
Complex[] com=new Complex[] {a1,b, a2, a3,b};
and the output is :
(-3.0+1.0i)+ (0.0+0.0i)X^1+(2.0+0.3i)X^2+(0.0+0.0i)X^3
But is supposed to be :
(-3.0+1.0i)+ (0.0+0.0i)X^1+(2.0+0.3i)X^2
And I've tried adding a "-1" to int a = coeff.length-j; :
int a = coeff.length-j-1;
but then if i print out
Complex[] com=new Complex[] {a1,b, a2, a3,b,b,b,b,b,b};
It's going to give me the same results (ie storing the trivial coefficients).
How can i make the contructor not store those trivial coefficients?
I think that the way to go in here is by iterating over the Array for the end to the start, just like you were trying. The problem with your code is the next:
if(coeff[i].getReal()== 0 && coeff[i].getImag() == 0 ){
j=+1; //Here! I think you wanted to do j+=1
}
At doing j=+1 you are making j to always have a value of 1. So, changing j=+1 to j+=1 will fix this.
Also, I did a different code if you want to check it. At the end, it does the same but I think is cleaner.
public class Polynomial {
private Complex[] coeff;
public Polynomial(Complex[] coeff) {
this.coeff = cleanCoeff(coeff);
}
private Complex[] cleanCoeff(Complex[] coeff) {
int length = coeff.length;
Complex complex = null;
for (int i = coeff.length - 1; i >= 0 ; i--) {
complex = coeff[i];
if(complex.getX() == 0 && complex.getY() == 0) {
length--;
}else {
break;
}
}
return Arrays.copyOf(coeff, length);
}
public Complex[] getCoeff() {
return coeff;
}
}
I hope this answer helps you.
This could be done relatively easily using something like the following:
int effective_len(Complex coeff[]) {
int pos = 0;
Complex zero();
for (int i=0; i<coeff.lengh; i++) {
if (!zero.equals(coeff[i])) {
pos = i;
}
}
return pos + 1;
}
For this you will need to define the equals method where you just check the real and imaginary components, but this should get you where you need to go.

What is the best way to check if ALL values in a range exist in an array? (java)

I have the task of determining whether each value from 1, 2, 3... n is in an unordered int array. I'm not sure if this is the most efficient way to go about this, but I created an int[] called range that just has all the numbers from 1-n in order at range[i] (range[0]=1, range[1]=2, ect). Then I tried to use the containsAll method to check if my array of given numbers contains all of the numbers in the range array. However, when I test this it returns false. What's wrong with my code, and what would be a more efficient way to solve this problem?
public static boolean hasRange(int [] givenNums, int[] range) {
boolean result = true;
int n = range.length;
for (int i = 1; i <= n; i++) {
if (Arrays.asList(givenNums).containsAll(Arrays.asList(range)) == false) {
result = false;
}
}
return result;
}
(I'm pretty sure I'm supposed to do this manually rather than using the containsAll method, so if anyone knows how to solve it that way it would be especially helpful!)
Here's where this method is implicated for anyone who is curious:
public static void checkMatrix(int[][] intMatrix) {
File numberFile = new File("valid3x3") ;
intMatrix= readMatrix(numberFile);
int nSquared = sideLength * sideLength;
int[] values = new int[nSquared];
int[] range = new int[nSquared];
int valCount = 0;
for (int i = 0; i<sideLength; i++) {
for (int j=0; j<sideLength; j++) {
values[valCount] = intMatrix[i][j];
valCount++;
}
}
for (int i=0; i<range.length; i++) {
range[i] = i+1;
}
Boolean valuesThere = hasRange(values, range);
valuesThere is false when printed.
First style:
if (condition == false) // Works, but at the end you have if (true == false) or such
if (!condition) // Better: not condition
// Do proper usage, if you have a parameter, do not read it in the method.
File numberFile = new File("valid3x3") ;
intMatrix = readMatrix(numberFile);
checkMatrix(intMatrix);
public static void checkMatrix(int[][] intMatrix) {
int nSquared = sideLength * sideLength;
int[] values = new int[nSquared];
Then the problem. It is laudable to see that a List or even better a Set approach is the exact abstraction level: going into detail not sensible. Here however just that is wanted.
To know whether every element in a range [1, ..., n] is present.
You could walk through the given numbers,
and for every number look whether it new in the range, mark it as no longer new,
and if n new numbers are reached: return true.
int newRangeNumbers = 0;
boolean[] foundRangeNumbers = new boolean[n]; // Automatically false
Think of better names.
You say you have a one dimensional array right?
Good. Then I think you are thinking to complicated.
I try to explain you another way to check if all numbers in an array are in number order.
For instance you have the array with following values:
int[] array = {9,4,6,7,8,1,2,3,5,8};
First of all you can order the Array simpel with
Arrays.sort(array);
After you've done this you can loop through the array and compare with the index like (in a method):
for(int i = array[0];i < array.length; i++){
if(array[i] != i) return false;
One way to solve this is to first sort the unsorted int array like you said then run a binary search to look for all values from 1...n. Sorry I'm not familiar with Java so I wrote in pseudocode. Instead of a linear search which takes O(N), binary search runs in O(logN) so is much quicker. But precondition is the array you are searching through must be sorted.
//pseudocode
int range[N] = {1...n};
cnt = 0;
while(i<-inputStream)
int unsortedArray[cnt]=i
cnt++;
sort(unsortedArray);
for(i from 0 to N-1)
{
bool res = binarySearch(unsortedArray, range[i]);
if(!res)
return false;
}
return true;
What I comprehended from your description is that the array is not necessarily sorted (in order). So, we can try using linear search method.
public static void main(String[] args){
boolean result = true;
int[] range <- Contains all the numbers
int[] givenNums <- Contains the numbers to check
for(int i=0; i<givenNums.length; i++){
if(!has(range, givenNums[i])){
result = false;
break;
}
}
System.out.println(result==false?"All elements do not exist":"All elements exist");
}
private static boolean has(int[] range, int n){
//we do linear search here
for(int i:range){
if(i == n)
return true;
}
return false;
}
This code displays whether all the elements in array givenNums exist in the array range.
Arrays.asList(givenNums).
This does not do what you think. It returns a List<int[]> with a single element, it does not box the values in givenNums to Integer and return a List<Integer>. This explains why your approach does not work.
Using Java 8 streams, assuming you don't want to permanently sort givens. Eliminate the copyOf() if you don't care:
int[] sorted = Arrays.copyOf(givens,givens.length);
Arrays.sort(sorted);
boolean result = Arrays.stream(range).allMatch(t -> Arrays.binarySearch(sorted, t) >= 0);
public static boolean hasRange(int [] givenNums, int[] range) {
Set result = new HashSet();
for (int givenNum : givenNums) {
result.add(givenNum);
}
for (int num : range) {
result.add(num);
}
return result.size() == givenNums.length;
}
The problem with your code is that the function hasRange takes two primitive int array and when you pass primitive int array to Arrays.asList it will return a List containing a single element of type int[]. In this containsAll will not check actual elements rather it will compare primitive array object references.
Solution is either you create an Integer[] and then use Arrays.asList or if that's not possible then convert the int[] to Integer[].
public static boolean hasRange(Integer[] givenNums, Integer[] range) {
return Arrays.asList(givenNums).containsAll(Arrays.asList(range));
}
Check here for sample code and output.
If you are using ApacheCommonsLang library you can directly convert int[] to Integer[].
Integer[] newRangeArray = ArrayUtils.toObject(range);
A mathematical approach: if you know the max value (or search the max value) check the sum. Because the sum for the numbers 1,2,3,...,n is always equal to n*(n+1)/2. So if the sum is equal to that expression all values are in your array and if not some values are missing. Example
public class NewClass12 {
static int [] arr = {1,5,2,3,4,7,9,8};
public static void main(String [] args){
System.out.println(containsAllValues(arr, highestValue(arr)));
}
public static boolean containsAllValues(int[] arr, int n){
int sum = 0;
for(int k = 0; k<arr.length;k++){
sum +=arr[k];
}
return (sum == n*(n+1)/2);
}
public static int highestValue(int[]arr){
int highest = arr[0];
for(int i = 0; i < arr.length; i++) {
if(highest<arr[i]) highest = arr[i];
}
return highest;
}
}
according to this your method could look like this
public static boolen hasRange (int [] arr){
int highest = arr[0];
int sum = 0;
for(int i = 0; i < arr.length; i++) {
if(highest<arr[i]) highest = arr[i];
}
for(int k = 0; k<arr.length;k++){
sum +=arr[k];
}
return (sum == highest *(highest +1)/2);
}

Generic Number Tweening (Help me do one strange trick..)

I am trying to make as generic as possible method for tweening between various types of values.
So, given a start and end value thats, say, either an Int,Float or Double as well as the number of steps (int), it will return values evenly distributed along those steps in the same type.
However, I am starting to suspect;
a) My knowledge of generics is terrible.
b) This might not be possible :(
So, just to be clear, one example;
SpiffyTween<Double> meep = new SpiffyTween<Double>(1d,10d, 100);
while (meep.hasNext()){
Log.info("value="+meep.next());
}
Would return 0.0,0.1,0.2..etc upto 9.9
But SpiffyTween could also work with other number types without needing separate code for each.
Heres the code I have right now;
class SpiffyTween<T extends Number> implements SpiffyGenericTween<T>
{
static Logger Log = Logger.getLogger("SpiffyTween <Number>");
private T start;
private T end;
int totalsteps=0;
int CurrentStep = 0;
ArrayList<T> steps = new ArrayList<T>();
public SpiffyTween(T start,T end, int steps) {
this.start = start;
this.end = end;
this.totalsteps = steps;
precalculate();
}
private void precalculate() {
//calc step difference
double dif = ((end.doubleValue() -start.doubleValue())/totalsteps);
Log.info("dif="+dif);
int i=0;
while(i<totalsteps){
T stepvalue = (T)((Number)(start.doubleValue() +(dif*i)));
steps.add(stepvalue);
Log.info("add step="+stepvalue);
i++;
}
}
public T next(){
T currentVal = steps.get(CurrentStep);
CurrentStep++;
return currentVal;
}
#Override
public boolean hasNext() {
if (CurrentStep<totalsteps){
return true;
}
return false;
}
}
This works...ish.
While the numbers come out aproximately right occasionally theres values like;
9.600000000000001
or
2.4000000000000004
I am assuming thats to do with the unchecked type conversion here;
T stepvalue = (T)((Number)(start.doubleValue() +(dif*i)));
But I cant work out how to do it better.
Whatever the solution (if theres one), my longterm plan is to try to make similar code that can also work on arrays of various number types. So, you could tween between 3 dimensional points by feeding it an array of the x/y/z co-ordinates of the start and end.
Also, possibly more relevantly, in the code example here its basic addition being done. I probably want other types of tweening possible, so that would make the maths more complex.
Is the better route to convert to, say, BigNumber, and then (somehow) back to the initial T later after all the processing is done?
Thanks in advance for any help or pointers.
YOu don't really need Generics to write code once. Consider the code below. Your exercise is to extend to other dimensions and to ensure caller does not use less than one step:
Tween Class
package com.example.stepup;
public class Tween {
public static int[] get1DimSteps (int start, int end, int steps) {
double[] preciseResult = get1DimSteps((double) start, (double) end, steps);
int[] result = new int[steps];
for (int i=0; i<steps; i++) {
result[i] = (int) (preciseResult[i] + 0.5D);
}
return result;
}
public static double[] get1DimSteps (float start, float end, int steps) {
double[] result = get1DimSteps((double)start, (double)end, steps);
return result;
}
public static double[] get1DimSteps (double start, double end, int steps) {
double distance;
double stepSize;
double[] result = new double[steps];
distance = end - start;
stepSize = distance / steps;
for (int i=0; i < steps; i++) {
result[i] = start + stepSize*i;
}
return result;
}
}
StepupTest Class
package com.example.stepup;
public class StepupTest {
public static void main(String[] args) {
// get steps from "start" to "finish"
int startI = -1;
int endI =999;
float start = (float) startI;
float end = (float) endI;
double startD = (double) startI;
double endD = (double) endI;
int numberOfSteps = 100;
double[] steps = Tween.get1DimSteps( start, end, numberOfSteps);
double[] stepsD = Tween.get1DimSteps(startD, endD, numberOfSteps);
int[] stepsI = Tween.get1DimSteps(startI, endI, numberOfSteps);
for (int i=0; i < numberOfSteps; i++) {
System.out.println(" " + i + ". " + steps[i] + ", " + stepsD[i] + ", " + stepsI[i]);
}
}
}

How to create a number generator that will only pick a number 1 time?

I am creating a concentration game.
I have an buffered image array where I load in a 25 image sprite sheet.
public static BufferedImage[] card = new BufferedImage[25];
0 index being the card back. and 1 - 24 being the values for the face of the cards to check against if the cards match.
What I am tying to do is this I will have 4 difficulties Easy, Normal, Hard, and Extreme. Each difficulty will have a certain amount of cards it will need to draw and then double the ones it chosen. for example the default level will be NORMAL which is 12 matches so it need to randomly choose 12 unique cards from the Buffered Image array and then double each value so it will only have 2 of each cards and then shuffle the results.
This is what I got so far but it always seems to have duplicates about 99% of the time.
//generate cards
Random r = new Random();
int j = 0;
int[] rowOne = new int[12];
int[] rowTwo = new int[12];
boolean[] rowOneBool = new boolean[12];
for(int i = 0; i < rowOneBool.length; i++)
rowOneBool[i] = false;
for(int i = 0; i < rowOne.length; i++){
int typeId = r.nextInt(12)+1;
while(rowOneBool[typeId]){
typeId = r.nextInt(12)+1;
if(rowOneBool[typeId] == false);
}
rowOne[i] = typeId;
j=0;
}
the 3 amounts I will be needing to generate is Easy 6, Normal 12, and Hard 18 extreme will use all of the images except index 0 which is the back of the cards.
This is more or less in the nature of random numbers. Sometimes they are duplicates. You can easily factor that in though if you want them to be more unique. Just discard the number and generate again if it's not unique.
Here's a simple method to generate unique random numbers with a specified allowance of duplicates:
public static void main(String[] args) {
int[] randoms = uniqueRandoms(new int[16], 1, 25, 3);
for (int r : randoms) System.out.println(r);
}
public static int[] uniqueRandoms(int[] randoms, int lo, int hi, int allowance) {
// should do some error checking up here
int range = hi - lo, duplicates = 0;
Random gen = new Random();
for (int i = 0, k; i < randoms.length; i++) {
randoms[i] = gen.nextInt(range) + lo;
for (k = 0; k < i; k++) {
if (randoms[i] == randoms[k]) {
if (duplicates < allowance) {
duplicates++;
} else {
i--;
}
break;
}
}
}
return randoms;
}
Edit: Tested and corrected. Now it works. : )
From what I understand from your question, the answer should look something like this:
Have 2 classes, one called Randp and the other called Main. Run Main, and edit the code to suit your needs.
package randp;
public class Main {
public static void main(String[] args) {
Randp randp = new Randp(10);
for (int i = 0; i < 10; i++) {
System.out.print(randp.nextInt());
}
}
}
package randp;
public class Randp {
private int numsLeft;
private int MAX_VALUE;
int[] chooser;
public Randp(int startCounter) {
MAX_VALUE = startCounter; //set the amount we go up to
numsLeft = startCounter;
chooser = new int[MAX_VALUE];
for (int i = 1; i <= chooser.length; i++) {
chooser[i-1] = i; //fill the array up
}
}
public int nextInt() {
if(numsLeft == 0){
return 0; //nothing left in the array
}
int a = chooser[(int)(Math.random() * MAX_VALUE)]; //picking a random index
if(a == 0) {
return this.nextInt(); //we hit an index that's been used already, pick another one!
}
chooser[a-1] = 0; //don't want to use it again
numsLeft--; //keep track of the numbers
return a;
}
}
This is how I would handle it. You would move your BufferedImage objects to a List, although I would consider creating an object for the 'cards' you're using...
int removalAmount = 3; //Remove 3 cards at random... Use a switch to change this based upon difficulty or whatever...
List<BufferedImage> list = new ArrayList<BufferedImage>();
list.addAll(Arrays.asList(card)); // Add the cards to the list, from your array.
Collections.shuffle(list);
for (int i = 0; i < removalAmount; i++) {
list.remove(list.size() - 1);
}
list.addAll(list);
Collections.shuffle(list);
for (BufferedImage specificCard : list) {
//Do something
}
Ok, I said I'd give you something better, and I will. First, let's improve Jeeter's solution.
It has a bug. Because it relies on 0 to be the "used" indicator, it won't actually produce index 0 until the end, which is not random.
It fills an array with indices, then uses 0 as effectively a boolean value, which is redundant. If a value at an index is not 0 we already know what it is, it's the same as the index we used to get to it. It just hides the true nature of algorithm and makes it unnecessarily complex.
It uses recursion when it doesn't need to. Sure, you can argue that this improves code clarity, but then you risk running into a StackOverflowException for too many recursive calls.
Thus, I present an improved version of the algorithm:
class Randp {
private int MAX_VALUE;
private int numsLeft;
private boolean[] used;
public Randp(int startCounter) {
MAX_VALUE = startCounter;
numsLeft = startCounter;
// All false by default.
used = new boolean[MAX_VALUE];
}
public int nextInt() {
if (numsLeft <= 0)
return 0;
numsLeft--;
int index;
do
{
index = (int)(Math.random() * MAX_VALUE);
} while (used[index]);
return index;
}
}
I believe this is much easier to understand, but now it becomes clear the algorithm is not great. It might take a long time to find an unused index, especially when we wanted a lot of values and there's only a few left. We need to fundamentally change the way we approach this. It'd be better to generate the values randomly from the beginning:
class Randp {
private ArrayList<Integer> chooser = new ArrayList<Integer>();
private int count = 0;
public Randp(int startCounter) {
for (int i = 0; i < startCounter; i++)
chooser.add(i);
Collections.shuffle(chooser);
}
public int nextInt() {
if (count >= chooser.size())
return 0;
return chooser.get(count++);
}
}
This is the most efficient and extremely simple since we made use of existing classes and methods.

Categories

Resources