Hi I have got merge Sort class I want to plot histogram of sorting array and when try to retrieve data from sorting class on screen appearing only unsorted array and then sorted array. How I have to restructure my sorting class so the full array will be returned on every occurrence of sorting or how I can retrieve merge(array, left, right) (I think this is the part I have to use to create histogram)
package mergeSort;
import java.util.*;
public class MergeSort {
public static void main(String[] args) {
int[] list = {14, 32, 67, 76, 23, 41, 58, 85};
System.out.println("before: " + Arrays.toString(list));
mergeSort(list);
System.out.println("after: " + Arrays.toString(list));
}
public static void mergeSort(int[] array) {
if (array.length > 1) {
// split array into two halves
int[] left = leftHalf(array);
int[] right = rightHalf(array);
// recursively sort the two halves
mergeSort(left);
mergeSort(right);
// merge the sorted halves into a sorted whole
merge(array, left, right);
}
}
// Returns the first half of the given array.
public static int[] leftHalf(int[] array) {
int size1 = array.length / 2;
int[] left = new int[size1];
for (int i = 0; i < size1; i++) {
left[i] = array[i];
}
return left;
}
// Returns the second half of the given array.
public static int[] rightHalf(int[] array) {
int size1 = array.length / 2;
int size2 = array.length - size1;
int[] right = new int[size2];
for (int i = 0; i < size2; i++) {
right[i] = array[i + size1];
}
return right;
}
public static void merge(int[] result, int[] left, int[] right) {
int i1 = 0; // index into left array
int i2 = 0; // index into right array
for (int i = 0; i < result.length; i++) {
if (i2 >= right.length || (i1 < left.length &&
left[i1] <= right[i2])) {
result[i] = left[i1]; // take from left
i1++;
} else {
result[i] = right[i2]; // take from right
i2++;
}
}
}
}
Also when I call for example bubble sort everything working fine so I think I must restructure the mergeClass
Please share if you have an idea thank you
This is my printing method
int Value = 100;
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int edge= 50;
for (int i = 0; i < array.length; i++) {
g.drawLine(i + offSet, Value + edge, i + edge,
Value + edge- array[i]);
}
}
Bubble sort
public void bubbleSort(int[] a) throws InterruptedException {
for (int i = 0; i < a.length; i++) {
for (int j = 1; j < (a.length - i); j++) {
if (a[j - 1] > a[j])
swap(a, j - 1, j);
}
}
}
Would keeping a record of each step (rather than returning it each step) suffice? If so the following would give you a List of int[] arrays, each storing the value at each step of the sorting process. You could then iterate over the list and display it at your leisure.
The following:
package mergeSort;
import java.util.*;
public class MergeSort {
public static void main(String[] args) {
int[] list = {14, 32, 67, 76, 23, 41, 58, 85};
List<int[]> listOfLists = new ArrayList<int[]>();
System.out.println("before: " + Arrays.toString(list));
mergeSort(list,listOfLists);
System.out.println("after: " + Arrays.toString(list));
System.out.println("Each step:");
for(int[] arrList : listOfLists)
System.out.println(Arrays.toString(arrList));
}
public static void mergeSort(int[] array, List<int[]> listOfLists) {
if (array.length > 1) {
// split array into two halves
int[] left = leftHalf(array);
int[] right = rightHalf(array);
// recursively sort the two halves
mergeSort(left,listOfLists);
mergeSort(right,listOfLists);
// merge the sorted halves into a sorted whole
merge(array, left, right);
listOfLists.add(array);
}
}
// Returns the first half of the given array.
public static int[] leftHalf(int[] array) {
int size1 = array.length / 2;
int[] left = new int[size1];
for (int i = 0; i < size1; i++) {
left[i] = array[i];
}
return left;
}
// Returns the second half of the given array.
public static int[] rightHalf(int[] array) {
int size1 = array.length / 2;
int size2 = array.length - size1;
int[] right = new int[size2];
for (int i = 0; i < size2; i++) {
right[i] = array[i + size1];
}
return right;
}
public static void merge(int[] result, int[] left, int[] right) {
int i1 = 0; // index into left array
int i2 = 0; // index into right array
for (int i = 0; i < result.length; i++) {
if (i2 >= right.length || (i1 < left.length &&
left[i1] <= right[i2])) {
result[i] = left[i1]; // take from left
i1++;
} else {
result[i] = right[i2]; // take from right
i2++;
}
}
}
}
Would return this:
before: [14, 32, 67, 76, 23, 41, 58, 85]
after: [14, 23, 32, 41, 58, 67, 76, 85]
Each step:
[14, 32]
[67, 76]
[14, 32, 67, 76]
[23, 41]
[58, 85]
[23, 41, 58, 85]
[14, 23, 32, 41, 58, 67, 76, 85]
Ok, I think I understand the problem you have. Does this work for you? Where I've put the println statement you can instead render the current array.
package mergeSort;
import java.util.Arrays;
public class MergeSort {
public static void main(String[] args) {
int[] list = { 14, 32, 76, 67, 41, 23, 58, 85 };
System.out.println("before: " + Arrays.toString(list));
mergeSort(list, 0, list.length - 1);
System.out.println("after: " + Arrays.toString(list));
}
public static void mergeSort(int[] array, int left, int right) {
int mid = 0;
if (right > left) {
mid = (right + left) / 2;
// sort left
mergeSort(array, left, mid);
// sort right
mergeSort(array, mid + 1, right);
// merge them
merge(array, left, mid+1, right);
// PUT YOUR HOOK TO DRAW THE ARRAY HERE
System.out.println("during: " + Arrays.toString(array));
}
}
public static void merge(int[] numbers, int left, int mid, int right) {
int[] temp = new int[numbers.length];
int i, left_end, num_elements, tmp_pos;
left_end = (mid - 1);
tmp_pos = left;
num_elements = (right - left + 1);
while ((left <= left_end) && (mid <= right)) {
if (numbers[left] <= numbers[mid])
temp[tmp_pos++] = numbers[left++];
else
temp[tmp_pos++] = numbers[mid++];
}
while (left <= left_end)
temp[tmp_pos++] = numbers[left++];
while (mid <= right)
temp[tmp_pos++] = numbers[mid++];
for (i = 0; i < num_elements; i++){
numbers[right] = temp[right];
right--;
}
}
}
The output that is produced is this:
before: [14, 32, 76, 67, 41, 23, 58, 85]
during: [14, 32, 76, 67, 41, 23, 58, 85]
during: [14, 32, 67, 76, 41, 23, 58, 85]
during: [14, 32, 67, 76, 41, 23, 58, 85]
during: [14, 32, 67, 76, 23, 41, 58, 85]
during: [14, 32, 67, 76, 23, 41, 58, 85]
during: [14, 32, 67, 76, 23, 41, 58, 85]
during: [14, 23, 32, 41, 58, 67, 76, 85]
after: [14, 23, 32, 41, 58, 67, 76, 85]
Related
int[] bestTime = {50, 73, 72, 75, 71, 56, 61, 60, 62, 68, 70, 50, 70};
assume if n = 6, expected return = {50, 50, 56, 60, 61, 62}
this is what i have so far, i know there are lots of mistakes. any suggestions is much appreciated.
public static int[] bestRun(int n) {
int[] best = bestTime[0];
for(int i = 0; i <= bestTime.length; i++ ) {
if(bestTime[i] <= best) {
best = bestTime[i];
best++;
} return best;
}
if(best.length == n) {
return best;
}
return null;
}
Build an IntStream of your bestTime array, sort them, limit using n, convert to array and return:
public static int[] bestRun(int n) {
return IntStream.of(bestTime).sorted().limit(n).toArray();
}
You can do the task also using classic for loops. But then you need to implement the sorting yourself. Something like below should give you a how this can be accomplished:
static int[] bestTime = {50, 73, 72, 75, 71, 56, 61, 60, 62, 68, 70, 50, 70};
public static void main(String args[]) throws IOException{
int[] best = bestRun(6);
System.out.println(Arrays.toString(best));
}
public static int[] bestRun(int n) {
//copy your bestTime array
int[] copy = new int[bestTime.length];
for(int i = 0; i < copy.length; i++){
copy[i] = bestTime[i];
}
//sort copy
for (int i = 0; i < copy.length; i++) {
for (int j = i+1; j < copy.length; j++) {
int temp = 0;
if(copy[i] > copy[j]) {
temp = copy[i];
copy[i] = copy[j];
copy[j] = temp;
}
}
}
//fill your result array
int[] result = new int[n];
for(int i = 0; i < n; i++){
result[i] = copy[i];
}
return result;
}
So I have this dummy 2D array:
int mat[][] = {
{10, 20, 30, 40, 50, 60, 70, 80, 90},
{15, 25, 35, 45},
{27, 29, 37, 48},
{32, 33, 39, 50, 51, 89}};
I want to add up all the values by columns so it would add 10 + 15 + 27 + 32 and return 84 and so on. I have this so far:
public void sum(int[][] array) {
int count = 0;
for (int rows = 0; rows < array.length; rows++) {
for (int columns = 0; columns < array[rows].length; columns++) {
System.out.print(array[rows][columns] + "\t");
count += array[0][0];
}
System.out.println();
System.out.println("total = " + count);
}
}
Can anyone help with this? Also the System.out.print(array[rows][columns] + "\t" ); prints the array out by rows, is there a way to print it out by columns?
One possible Solution would be to first find maximum size of all sub arrays and iterate that many times to find sum of each column avoiding unavailable values.
public static void main(String[] args) {
int mat[][] = {{10, 20, 30, 40, 50, 60, 70, 80, 90},
{15, 25, 35, 45},
{27, 29, 37, 48},
{32, 33, 39, 50, 51, 89},
};
// Find maximum possible length of sub array
int maxLength = 0;
for (int i = 0; i < mat.length; i++) {
if (maxLength < mat[i].length)
maxLength = mat[i].length;
}
for (int i = 0; i < maxLength; i++) {
int sum = 0;
for (int j = 0; j < mat.length; j++) {
// Avoid if no value available for
// ith column from this subarray
if (i < mat[j].length)
sum += mat[j][i];
}
System.out.println("Sum of Column " + i + " = " + sum);
}
}
Use an ArrayList to get the sum of all the columns.
public static void sum(int[][] array) {
ArrayList<Integer> sums = new ArrayList<>();
for (int row = 0; row < array.length; row++) {
for (int column = 0; column < array[row].length; column++) {
if (sums.size() <= column) {
sums.add(column, 0);
}
int curVal = sums.get(column);
sums.remove(column);
sums.add(column, curVal + array[row][column]);
}
}
for (int i = 0; i < sums.size(); i++) {
System.out.println("Sum of column " + i + " = " + sums.get(i));
}
}
Here is one alternative.
The supplied data.
int mat[][] = { { 10, 20, 30, 40, 50, 60, 70, 80, 90 },
{ 15, 25, 35, 45 }, { 27, 29, 37, 48 },
{ 32, 33, 39, 50, 51, 89 }, };
First, find the maximum length of the array in which to store the sum.
int max = Arrays.stream(mat).mapToInt(a -> a.length).max().orElse(0);
Allocate the new array to hold the sums.
int[] sums = new int[max];
Now just use the Arrays.setAll method to sum them, taking careto not exceed the current array's length.
for (int[] arr : mat) {
Arrays.setAll(sums, i-> i < arr.length ? sums[i] + arr[i] : sums[i]);
}
System.out.println(Arrays.toString(sums));
Prints
[84, 107, 141, 183, 101, 149, 70, 80, 90]
You can use Stream.reduce method to summarise the elements of the rows of the matrix by the columns:
int[][] matrix = {
{10, 20, 30, 40, 50, 60, 70, 80, 90},
{15, 25, 35, 45},
{27, 29, 37, 48},
{32, 33, 39, 50, 51, 89}};
int[] arr = Arrays.stream(matrix)
// summarize in pairs
// the rows of the matrix
.reduce((row1, row2) -> IntStream
// iterate over the indices
// from 0 to maximum row length
.range(0, Math.max(row1.length, row2.length))
// summarize in pairs the elements of two rows
.map(i -> (i < row1.length ? row1[i] : 0) +
(i < row2.length ? row2[i] : 0))
// an array of sums
.toArray())
// the resulting array
.get();
System.out.println(Arrays.toString(arr));
// [84, 107, 141, 183, 101, 149, 70, 80, 90]
See also:
• Sum of 2 different 2d arrays
• How to calculate the average value of each column in 2D array?
…and the same with lambda:
int[] array = Arrays.stream(mat)
.reduce((a1, a2) -> IntStream.range(0, Math.max(a1.length, a2.length))
.map(i -> i < a1.length && i < a2.length ? a1[i] + a2[i]
: i < a1.length ? a1[i] : a2[i] ).toArray()).get();
gets the sums of each column in the int[] array:
[84, 107, 141, 183, 101, 149, 70, 80, 90]
I'd really appreciate some help on this assignment.
The task is develop a merge sort algorithm that "splits" the input array recursively into k arrays. I initially coded two methods to merge sort with two parts, namely merge sort and merge. I tried to generalize this algorithm
public class MergeSort {
public static int[] mergeSort(int[] a, int p, int r) {
// p is left bound
// r is right bound
if (p < r) {
int q = (int)Math.floor((p + r) / 2);
mergeSort(a, p, q);
mergeSort(a, q + 1, r);
return merge(a, p, q, r);
} else
return a;
}
// p ist linke grenze
// r ist rechte grenze
public static int[] merge(int[] a, int p, int q, int r) {
int n1 = q - p + 1; //length of first array
int n2 = r - q; //length of second array
int[] lt = new int[n1 + 1];
int[] rt = new int[n2 + 1];
for (int i = 0; i < n1; i++) {
lt[i] = a[p + i];
}
for (int j = 0; j < n2; j++) {
rt[j] = a[q + j + 1];
}
lt[n1] = 1000000000; //sentinels
rt[n2] = 1000000000;
int i = 0;
int j = 0;
for (int k = p; k <= r; k++) { //comparing the values of the arrays and merging
if (lt[i] <= rt[j]) {
a[k] = lt[i];
i++;
} else {
a[k] = rt[j];
j++;
}
}
return a;
}
public static int[] mergeSortK(int[] a, int k, int p, int r) {
// k number of steps; p is first index of array; r is last index of array;
if (p < r) {
int[] pos = new int[k + 1]; //array for saving the indices of the "splits"
for (int i = 0; i <= k; i++) {
pos[i] = (int) Math.floor(p + (r - p) / k * i); //saving the array indices
}
for (int i = 0; i < k; i++) {
mergeSortK(a, k, pos[i], pos[i + 1]); //sorting the arrays
}
for (int i = 0; i < k - 1; i++) {
merge(a, pos[i], pos[i + 1], pos[i + 2]); //merging the arrays pairwise
}
}
return a;
}
public static void main(String[] args) {
// task 2.1.a)
// Example Values:
int[] list = { 2, 1, 5, 6, 2, 12 };
int k = 4;
// use MergeSort
int[] newlist = mergeSortK(list, k, 0, list.length);
printList(newlist);
}
// Helper function to print the elements of a list
private static void printList(int[] list) {
for(int i = 0; i < list.length; i++) {
System.out.println(list[i]);
}
}
}
The input given in the main method results in {2, 1, 2, 5, 6, 12}
Any help is immensely appreciated! Sorry if I'm doing some mistakes, I'm here to learn and I really hope you guys can help me out!
There are a few problems in your code:
there is no need for Math.floor() to truncate the results of integer division, unlike Javascript, Java uses different semantics for / for int arguments and floating point arguments. (p + r) / 2 is the integral quotient, But you might want to write p + (r - p) / 2 to avoid potential overflow on p + r.
the upper index is excluded as you pass list.length from main(). This is actually a very convenient convention to avoid the need for +1 adjustments when computing slice sizes. Remove those erroneous adjustments and rely on the included/excluded convention.
don't use sentinels: using sentinels prevents you from correctly sorting arrays containing values greater or equal to the sentinel value 1000000000. This approach is not necessary and should be banned. Just compare the index variables to the slice lengths and copy the remaining elements when one of the slices is exhausted.
your computation for the slice boundaries in mergeSortK is incorrect: p + (r - p) / k * i is computed with integer arithmetics so (r - p) / k is rounded before the multiplication. The last slice ending index will not equal r if r - p is not a multiple of k. Multiplying before the division would solve this issue but might overflow the range of type int.
mergeSortK does not perform k-way merging, but a series of partial merges that are insufficient for k > 2.
your test set is a bit small.
Here is a corrected version:
public class MergeSort {
public static int[] mergeSort(int[] a, int p, int r) {
// p is left bound (included)
// r is right bound (excluded)
if (r - p >= 2) {
int q = p - (r - p) / 2;
mergeSort(a, p, q);
mergeSort(a, q, r);
return merge(a, p, q, r);
} else {
return a;
}
}
// p is left bound (included)
// q is start of right slice
// r is end of right slice (excluded)
public static int[] merge(int[] a, int p, int q, int r) {
int n1 = q - p; // length of first array
int n2 = r - q; // length of second array
int[] lt = new int[n1];
for (int i = 0; i < n1; i++) {
lt[i] = a[p + i];
}
int i = 0; // index into lt
int j = q; // index into a for right slice
int k = p; // index into a for merged list
while (i < n1 && j < r) { //comparing the values of the arrays and merging
if (lt[i] <= a[j]) {
a[k] = lt[i];
i++;
k++;
} else {
a[k] = a[j];
j++;
k++;
}
}
while (i < n1) { // copy remaining elements from right slice
a[k] = lt[i];
i++;
k++;
}
// remaining elements from right slice are already in place
return a;
}
public static int[] mergeSortK(int[] a, int k, int p, int r) {
// k amount of steps; p is first index of slice; r is last index of slice (excluded);
if (r - p >= 2) {
if (k > r - p)
k = r - p;
int[] pos = new int[k + 1]; //array for saving the indices of the "splits"
for (int i = 0; i <= k; i++) {
pos[i] = p + (r - p) * i / k; //saving the array indices
}
for (int i = 0; i < k; i++) {
mergeSortK(a, k, pos[i], pos[i + 1]); //sorting the arrays
}
while (k > 1) {
int i, n = 1;
for (i = 0; i < k - 1; i += 2) {
// merge slices 2 at a time: this will produce the expected output
// but is not a direct k-way merge.
merge(a, pos[i], pos[i + 1], pos[i + 2]);
pos[n++] = pos[i + 2];
}
if (i < k)
pos[n++] = pos[i + 1];
k = n - 1;
}
}
return a;
}
public static void main(String[] args) {
// task 2.1.a)
// Example Values:
int[] list = {
64, 36, 46, 31, 45, 52, 4, 48, 74, 59,
12, 16, 70, 67, 71, 26, 73, 34, 46, 84,
60, 16, 26, 68, 56, 57, 97, 6, 39, 74,
25, 69, 29, 69, 77, 26, 44, 53, 20, 6,
77, 31, 71, 91, 28, 6, 24, 75, 26, 33,
3, 20, 55, 94, 17, 81, 88, 32, 94, 32,
3, 90, 76, 69, 9, 96, 76, 53, 78, 14,
97, 32, 17, 15, 61, 63, 21, 0, 16, 14,
61, 4, 81, 86, 29, 29, 27, 57, 85, 5,
91, 54, 6, 68, 40, 88, 41, 9, 90, 51 };
int k = 4; // must be at least 2
// use MergeSort
int[] newlist = mergeSortK(list, k, 0, list.length);
printList(newlist);
}
// Helper function to print the elements of a list
private static void printList(int[] list) {
for (int i = 0; i < list.length; i++) {
System.out.println(list[i]);
}
}
}
I did not write a correct k-way merging phase at the end of mergeSortK, the code above should work but will merge the k slices in ceil(log2(k)) passes. Direct one pass k-way merging is tricky and usually not worth it.
I'm working on a merge sort program but not giving the correct result, Also it is not splitting my list into two equal parts, Kindly take a look at below on my program:
import java.util.Arrays;
public class MyProgram {
public static void main(String[] args){
int[] list = {64, 7, 52, 68, 19, 10, 11, 14, 17, 18, 19};
System.out.println("\t**AFTER SPLITING**");
System.out.println("Left List: "+Arrays.toString(leftList(list)));
System.out.println("Right List: "+Arrays.toString(rightList(list)));
System.out.println("\n\n\t**BEFORE MERGE SORT**");
System.out.println(Arrays.toString(list));
mergeSort(list);
System.out.println("\t**AFTER MERGE SORT**");
System.out.println(Arrays.toString(list));
}
private static void mergeSort(int[] array){
if(array.length > 1){
int[] left = leftList(array);
int[] right = rightList(array);
mergeSort(left); //recursion
mergeSort(right); //recursion
//Merging the sorted half into equal parts.
merge(array, left, right);
}
}
private static int[] leftList(int[] array){
int size = array.length/2;
int left[] = new int[size];
for(int i=0; i<size; i++){
left[i] = array[i];
}
return left;
}
private static int[] rightList(int[] array){
int size1 = array.length/2;
int size2 = array.length - size1;
int right[] = new int[size2];
for(int i=0; i<size2; i++){
right[i] = array[i+size1];
}
return right;
}
private static void merge(int[] result, int[] left, int[] right){
int i1 = 0; //left index.
int i2 = 0; //right index.
for(int i=0; i<result.length; i++){
if(i1 < left.length && i2 < right.length){
if(left[i1] <= right[i2]){
result[i1] = left[i1];
i1++;
}
else{
result[i2] = right[i2];
i2++;
}
}
else if(i1 < left.length){
result[i1] = left[i1];
i1++;
}
else{
result[i2] = right[i2];
i2++;
}
}
}
}
It gives the following outputs below:
**AFTER SPLITING**
Left List: [64, 7, 52, 68, 19]
Right List: [10, 11, 14, 17, 18, 19]
**BEFORE MERGE SORT**
[64, 7, 52, 68, 19, 10, 11, 14, 17, 18, 19]
**AFTER MERGE SORT**
[68, 19, 19, 68, 19, 19, 11, 14, 17, 18, 19]
As you can see above that my leftList and rightList are splitting into equal parts but not sorting my result.
HELP WOULD BE APPRECIATED!!
In merge, you are using i1 and i2 to index into result, instead of i.
private static void merge(int[] result, int[] left, int[] right){
int i1 = 0; //left index.
int i2 = 0; //right index.
for(int i=0; i<result.length; i++){
if(i1 < left.length && i2 < right.length){
if(left[i1] <= right[i2]){
result[i] = left[i1];
i1++;
}
else{
result[i] = right[i2];
i2++;
}
}
else if(i1 < left.length){
result[i] = left[i1];
i1++;
}
else{
result[i] = right[i2];
i2++;
}
}
}
Not sure if this exact question's been asked before, though a similar question has been asked here. Essentially, what I'm trying to is generate random integers of a minimum size that still sum up to a certain value (an invariant is that the sum / (number of randoms you want) is greater than the minimum value. Here's a sad attempt I coded up:
import java.util.Arrays;
import java.util.Random;
public class ScratchWork {
private static Random rand = new Random();
public static void main(String[] args) {
int[] randoms = genRandoms(1000, 10, 30);
for (int i = 0; i<randoms.length; i++) sop("Random Number "+ (i+1) + ": " + randoms[i]);
sop("Sum: " + sum(randoms));
}
public static int sum(int[] array) { int sum = 0; for (int i : array) sum+=i; return sum; }
public static int[] genRandoms(int n, int numberOfRandoms, int min) {
if (min > n/numberOfRandoms) throw new UnsupportedOperationException();
int[] intRandArray = {0};
while (sum(intRandArray) != n) {
////////////////////////
// See https://stackoverflow.com/questions/2640053/getting-n-random-numbers-that-the-sum-is-m
Double[] randArray = new Double[numberOfRandoms];
double workerSum = 0;
for (int i = 0; i<numberOfRandoms; i++) {
randArray[i] = ((double)rand.nextInt(n-1)+1);
workerSum += randArray[i];
}
for (int i = 0; i<randArray.length; i++) {
randArray[i] = n*(randArray[i]/(workerSum));
}
/////////////////////////
while (existsSmallerNumThanMin(randArray, min))
randArray = continueToFix(randArray, min);
//Convert doubles to ints
intRandArray = new int [randArray.length];
for (int i = 0; i < intRandArray.length; i++)
intRandArray[i] = (int)Math.round(randArray[i]);
}
return intRandArray;
}
public static boolean existsSmallerNumThanMin(Double[] randArray, int min) {
for (double i : randArray) if (i < (double)min) return true;
return false;
}
public static Double[] continueToFix(Double[]randArray, int min) {
Double[] tempArray = Arrays.copyOf(randArray, randArray.length);
Arrays.sort(tempArray);
int smallest = Arrays.asList(randArray).indexOf(tempArray[0]);
int largest = Arrays.asList(randArray).indexOf(tempArray[tempArray.length-1]);
double randomDelta = rand.nextInt(min);
randArray[smallest]+=randomDelta;
randArray[largest]-=randomDelta;
return randArray;
}
public static void sop(Object s) { System.out.println(s); }
}
This is neither an elegant nor high-performing way to do this... It also doesn't seem to work well (if at all) when passed in, say, (100,10,10), which only allows for the number 10 in the array. The distribution of the random numbers is also pretty bad.
Is there an elegant approach to this??
Also, my end goal is to implement this in Objective-C, though I'm still just learning the ropes of that language, so any tips for doing this in that language would be greatly appreciated.
I did some more testing, and my Java implementation is broken. I think my algorithm is bad.
How about something like getting N random numbers in [0,1] and normalize the results based on their sums (different than the desired sum). Then multiply these values against the desired number sum.
This assumes the minimum size is 0. Generalizing this to maintain a minimum for each result is simple. Just feed in sum - min * n for sum. Add min to all the results.
To avoid potential floating point issues, you can do something similar with using a range of integer values that are logically on the scale [0,M] (M arbitarily large) instead of [0,1]. The idea is the same in that you "normalize" your random results. So lets say you get a random sum of N (want N to have to be a multiple of sum+1... see REMARK). Divy N up by sum+1 parts. The first part is 0, second is 1, ..., last is sum. Each random value looks up its value by seeing what part of N it belongs to.
REMARK: If you are okay with an arbitrarily small amount of bias, make each die roll a magnitude larger than sum (might want BigInteger for this). Let N be the sum. Then N = k*sum + rem where rem < sum. If N is large, then rem / k -> 0, which is good. N is probabilistically large if M is large.
Algorithm psuedo code:
F(S, N):
let R[] = list of 0's of size N
if S = 0: return R[]
for 0 <= i < N
R[i] = random integer in [0, M] -- inclusive and (M >= S). M should be an order larger than S. A good pick might be M = 1000*S*N. Not sure though. M should probably be based on both S and N though or simply an outragously large number.
let Z = sum R[]
for 0 <= i < N
R[i] = (Z mod R[i]) mod (S+1)
return R[]
Java implementation:
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Map;
import java.util.Random;
import java.util.TreeMap;
public class Rand {
private final Random rand;
public Rand () {
this(new Random());
}
public Rand (Random rand) {
this.rand = rand;
}
public int[] randNums (int total, int minVal, int numRands) {
if (minVal * numRands > total) {
throw new IllegalArgumentException();
}
int[] results = randNums(total - minVal * numRands, numRands);
for (int i = 0; i < numRands; ++i) {
results[i] += minVal;
}
return results;
}
private int[] randNums (int total, int n) {
final int[] results = new int[n];
if (total == 0) {
Arrays.fill(results, 0);
return results;
}
final BigInteger[] rs = new BigInteger[n];
final BigInteger totalPlus1 = BigInteger.valueOf(total + 1L);
while (true) {
for (int i = 0; i < n; ++i) {
rs[i] = new BigInteger(256, rand);
}
BigInteger sum = BigInteger.ZERO;
for (BigInteger r : rs) {
sum = sum.add(r);
}
if (sum.compareTo(BigInteger.ZERO) == 0) {
continue;
}
for (int i = 0; i < n; ++i) {
results[i] = sum.mod(rs[i]).mod(totalPlus1).intValue();
}
return results;
}
}
public static void main (String[] args) {
Rand rand = new Rand();
Map<Integer, Integer> distribution = new TreeMap<Integer, Integer>();
final int total = 107;
final int minVal = 3;
final int n = 23;
for (int i = total - (n-1) * minVal; i >= minVal; --i) {
distribution.put(i, 0);
}
for (int i = 0; i < 100000; ++i) {
int[] rs = rand.randNums(total, minVal, n);
for (int r : rs) {
int count = distribution.get(r);
distribution.put(r, count + 1);
}
}
System.out.print(distribution);
}
}
Does this do what you need?
import java.util.Arrays;
import java.util.Random;
public class ScratchWork {
static Random rng = new Random();
public static int randomRange(int n) {
// Random integer between 0 and n-1
assert n > 0;
int r = rng.nextInt();
if(r >= 0 && r < Integer.MAX_VALUE / n * n) {
return r % n;
}
return randomRange(n);
}
public static int[] genRandoms(int n, int numberOfRandoms, int min) {
int randomArray[] = new int[numberOfRandoms];
for(int i = 0; i < numberOfRandoms; i++) {
randomArray[i] = min;
}
for(int i = min*numberOfRandoms; i < n; i++) {
randomArray[randomRange(numberOfRandoms)] += 1;
}
return randomArray;
}
public static void main(String[] args) {
int randoms[] = genRandoms(1000, 10, 30);
System.out.println(Arrays.toString(randoms));
}
}
Based on the revised requirements in the comments below.
public static int[] genRandoms(int total, int numberOfRandoms, int minimumValue) {
int[] ret = new int[numberOfRandoms];
Random rnd = new Random();
int totalLeft = total;
for (int i = 0; i < numberOfRandoms; i++) {
final int rollsLeft = numberOfRandoms - i;
int thisMax = totalLeft - (rollsLeft - 1) * minimumValue;
int thisMin = Math.max(minimumValue, totalLeft / rollsLeft);
int range = thisMax - thisMin;
if (range < 0)
throw new IllegalArgumentException("Cannot have " + minimumValue + " * " + numberOfRandoms + " < " + total);
int rndValue = range;
for (int j = 0; j * j < rollsLeft; j++)
rndValue = rnd.nextInt(rndValue + 1);
totalLeft -= ret[i] = rndValue + thisMin;
}
Collections.shuffle(Arrays.asList(ret), rnd);
return ret;
}
public static void main(String... args) throws IOException {
for (int i = 100; i <= 1000; i += 150)
System.out.println("genRandoms(" + i + ", 30, 10) = " + Arrays.toString(genRandoms(1000, 30, 10)));
}
prints
genRandoms(100, 30, 10) = [34, 13, 22, 20, 26, 12, 30, 45, 22, 35, 108, 20, 31, 53, 11, 35, 20, 11, 18, 32, 14, 30, 20, 19, 31, 31, 151, 45, 25, 36]
genRandoms(250, 30, 10) = [30, 27, 25, 34, 28, 33, 34, 82, 45, 30, 24, 26, 26, 45, 19, 18, 95, 28, 22, 30, 30, 25, 38, 11, 18, 27, 77, 26, 26, 21]
genRandoms(400, 30, 10) = [48, 25, 19, 22, 36, 65, 24, 29, 49, 24, 11, 30, 33, 41, 37, 33, 29, 36, 28, 24, 32, 12, 28, 29, 29, 34, 35, 28, 27, 103]
genRandoms(550, 30, 10) = [25, 44, 72, 36, 55, 41, 11, 33, 20, 21, 33, 19, 29, 30, 13, 39, 54, 26, 33, 30, 40, 32, 21, 31, 61, 13, 16, 51, 37, 34]
genRandoms(700, 30, 10) = [22, 13, 24, 26, 23, 61, 44, 79, 69, 25, 29, 83, 29, 35, 25, 13, 33, 32, 13, 12, 30, 26, 28, 26, 14, 21, 26, 13, 84, 42]
genRandoms(850, 30, 10) = [11, 119, 69, 14, 39, 62, 51, 52, 34, 16, 12, 17, 28, 25, 17, 31, 32, 30, 34, 12, 12, 38, 11, 32, 25, 16, 31, 82, 18, 30]
genRandoms(1000, 30, 10) = [25, 46, 59, 48, 36, 32, 29, 12, 27, 34, 33, 14, 12, 30, 29, 31, 25, 16, 34, 44, 25, 50, 60, 32, 42, 32, 13, 41, 51, 38]
IMHO, Shuffling the result improves the randomness of the distribution.
Maybe a recursive approach:
if numberOfRandoms is 1, return n
if numberOfRandoms is x+1
get a random integer (myNumber) between min and n-min*x (so that at least min is left for each of the remaining x numbers)
get the remaining x that add up to n-myNumber
This is a utility method for generating a fixed length random number.
public final static String createRandomNumber(long len) {
if (len > 18)
throw new IllegalStateException("To many digits");
long tLen = (long) Math.pow(10, len - 1) * 9;
long number = (long) (Math.random() * tLen) + (long) Math.pow(10, len - 1) * 1;
String tVal = number + "";
if (tVal.length() != len) {
throw new IllegalStateException("The random number '" + tVal + "' is not '" + len + "' digits");
}
return tVal;
}