I'm a high school Computer Science student, and today I was given a problem to:
Program Description: There is a belief among dice players that in
throwing three dice a ten is easier to get than a nine. Can you write
a program that proves or disproves this belief?
Have the computer compute all the possible ways three dice can be
thrown: 1 + 1 + 1, 1 + 1 + 2, 1 + 1 + 3, etc. Add up each of these
possibilities and see how many give nine as the result and how many
give ten. If more give ten, then the belief is proven.
I quickly worked out a brute force solution, as such
int sum,tens,nines;
tens=nines=0;
for(int i=1;i<=6;i++){
for(int j=1;j<=6;j++){
for(int k=1;k<=6;k++){
sum=i+j+k;
//Ternary operators are fun!
tens+=((sum==10)?1:0);
nines+=((sum==9)?1:0);
}
}
}
System.out.println("There are "+tens+" ways to roll a 10");
System.out.println("There are "+nines+" ways to roll a 9");
Which works just fine, and a brute force solution is what the teacher wanted us to do. However, it doesn't scale, and I am trying to find a way to make an algorithm that can calculate the number of ways to roll n dice to get a specific number. Therefore, I started generating the number of ways to get each sum with n dice. With 1 die, there is obviously 1 solution for each. I then calculated, through brute force, the combinations with 2 and 3 dice. These are for two:
There are 1 ways to roll a 2 There are 2 ways to roll a 3
There are 3 ways to roll a 4 There are 4 ways to roll a 5
There are 5 ways to roll a 6 There are 6 ways to roll a 7
There are 5 ways to roll a 8 There are 4 ways to roll a 9
There are 3 ways to roll a 10 There are 2 ways to roll a 11
There are 1 ways to roll a 12
Which looks straightforward enough; it can be calculated with a simple linear absolute value function. But then things start getting trickier. With 3:
There are 1 ways to roll a 3 There are 3 ways to roll a 4
There are 6 ways to roll a 5 There are 10 ways to roll a 6
There are 15 ways to roll a 7 There are 21 ways to roll a 8
There are 25 ways to roll a 9 There are 27 ways to roll a 10
There are 27 ways to roll a 11 There are 25 ways to roll a 12
There are 21 ways to roll a 13 There are 15 ways to roll a 14
There are 10 ways to roll a 15 There are 6 ways to roll a 16
There are 3 ways to roll a 17 There are 1 ways to roll a 18
So I look at that, and I think: Cool, Triangular numbers! However, then I notice those pesky 25s and 27s. So it's obviously not triangular numbers, but still some polynomial expansion, since it's symmetric.
So I take to Google, and I come across this page that goes into some detail about how to do this with math. It is fairly easy(albeit long) to find this using repeated derivatives or expansion, but it would be much harder to program that for me. I didn't quite understand the second and third answers, since I have never encountered that notation or those concepts in my math studies before. Could someone please explain how I could write a program to do this, or explain the solutions given on that page, for my own understanding of combinatorics?
EDIT: I'm looking for a mathematical way to solve this, that gives an exact theoretical number, not by simulating dice
The solution using the generating-function method with N(d, s) is probably the easiest to program. You can use recursion to model the problem nicely:
public int numPossibilities(int numDice, int sum) {
if (numDice == sum)
return 1;
else if (numDice == 0 || sum < numDice)
return 0;
else
return numPossibilities(numDice, sum - 1) +
numPossibilities(numDice - 1, sum - 1) -
numPossibilities(numDice - 1, sum - 7);
}
At first glance this seems like a fairly straightforward and efficient solution. However you will notice that many calculations of the same values of numDice and sum may be repeated and recalculated over and over, making this solution probably even less efficient than your original brute-force method. For example, in calculating all the counts for 3 dice, my program ran the numPossibilities function a total of 15106 times, as opposed to your loop which only takes 6^3 = 216 executions.
To make this solution viable, you need to add one more technique - memoization (caching) of previously calculated results. Using a HashMap object, for example, you can store combinations that have already been calculated and refer to those first before running the recursion. When I implemented a cache, the numPossibilities function only runs 151 times total to calculate the results for 3 dice.
The efficiency improvement grows larger as you increase the number of dice (results are based on simulation with my own implemented solution):
# Dice | Brute Force Loop Count | Generating-Function Exec Count
3 | 216 (6^3) | 151
4 | 1296 (6^4) | 261
5 | 7776 (6^5) | 401
6 | 46656 (6^6) | 571
7 | 279936 (6^7) | 771
...
20 | 3656158440062976 | 6101
You don't need to brute force since your first roll determines what values can be used in the second roll, and both first and second roll determine the third roll. Let's take the tens example, suppose you roll a 6, so 10-6=4 meaning you still need 4. For the second roll you need at least 3, because your third roll should at least count for 1. So the second roll goes from 1 to 3. Suppose your second roll is 2, you have 10-6-2=2, meaning your third roll IS a 2, there is no other way.
Pseudo code for tens:
tens = 0
for i = [1..6] // first roll can freely go from 1 to 6
from_j = max(1, 10 - i - 6) // We have the first roll, best case is we roll a 6 in the third roll
top_j = min(6, 10 - i - 1) // we need to sum 10, minus i, minus at least 1 for the third roll
for j = [from_j..to_j]
tens++
Note that each loop adds 1, so at the end you know this code loops exactly 27 times.
Here is the Ruby code for all 18 values (sorry it's not Java, but it can be easily followed). Note the min and max, that determine what values can have each of the dice rolls.
counts = [0] * 18
1.upto(18) do |n|
from_i = [1, n - 6 - 6].max # best case is we roll 6 in 2nd and 3rd roll
top_i = [6, n - 1 -1].min # at least 1 for 2nd and 3rd roll
from_i.upto(top_i) do |i|
from_j = [1, n - i - 6].max # again we have the 2nd roll defined, best case for 3rd roll is 6
top_j = [6, n - i -1].min # at least 1 for 3rd roll
from_j.upto(top_j) do
# no need to calculate k since it's already defined being n - first roll - second roll
counts[n-1] += 1
end
end
end
print counts
For a mathematical approach, take a look at https://math.stackexchange.com/questions/4632/how-can-i-algorithmically-count-the-number-of-ways-n-m-sided-dice-can-add-up-t
Mathematical description is just a 'trick' to make same counting. It uses polynomial to express dice, 1*x^6 + 1*x^5 + 1*x^4 + 1*x^3 + 1*x^2 + 1*x means that each value 1-6 is counted once, and it uses polynomial multiplication P_1*P_2 for a counting of different combinations. That is done since coefficient at some exponent (k) is calculated by summing all coefficient in P_1 and P_2 which exponent sum to k.
E.g. for two dices we have:
(1*x^6 + 1*x^5 + 1*x^4 + 1*x^3 + 1*x^2 + 1*x) * (x^6 + x^5 + x^4 + x^3 + x^2 + x) =
(1*1)*x^12 + (1*1 + 1*1)*x^11 + (1*1 + 1*1 + 1*1)*x^11 + ... + (1*1 + 1*1)*x^3 + (1*1)*x^2
Calculation by this method has same complexity as 'counting' one.
Since function (x^6 + x^5 + x^4 + x^3 + x^2 + x)^n has simpler expression (x(x-1)^6/(x-1))^n, it is possible to use derivation approach. (x(x-1)^6/(x-1))^n is a polynomial, and we are looking for coefficient at x^s (a_s). Free coefficient (at x^0) of s'th derivation is s! * a_k. So, s'th derivation in 0 is s! * a_k.
So, we have to derive this function s times. It can be done using derivation rules, but I think that it will have even worse complexity than counting approach since each derivation produces 'more complex' function. Here are first three derivations from Wolfram Alpha: first, second and third.
In general, I prefer counting solution, and mellamokb gave nice approach and explanation.
Check out Monte Carlo Methods they usually scale linearly with inputsize. In this case the example is easy, we assume that since once throw of the dice doesn't affect the other instead of counting combinations we can simply count the sum of the faces of dices thrown randomly (many times enough).
public class MonteCarloDice {
private Map<Integer, Integer> histogram;
private Random rnd;
private int nDice;
private int n;
public MonteCarloDice(int nDice, int simulations) {
this.nDice = nDice;
this.n = simulations;
this.rnd = new Random();
this.histogram = new HashMap<>(1000);
start();
}
private void start() {
for (int simulation = 0; simulation < n; simulation++) {
int sum = 0;
for (int i = 0; i < nDice; i++) {
sum += rnd.nextInt(6) + 1;
}
if (histogram.get(sum) == null)
histogram.put(sum, 0);
histogram.put(sum, histogram.get(sum) + 1);
}
System.out.println(histogram);
}
public static void main(String[] args) {
new MonteCarloDice(3, 100000);
new MonteCarloDice(10, 1000000);
}
}
The error decreases with number of simulations but at the cost of cputime but the above values were pretty fast.
3 dice
{3=498, 4=1392, 5=2702, 6=4549, 7=7041, 8=9844, 9=11583, 10=12310, 11=12469, 12=11594, 13=9697, 14=6999, 15=4677, 17=1395, 16=2790, 18=460}
10 dice
{13=3, 14=13, 15=40, 17=192, 16=81, 19=769, 18=396, 21=2453, 20=1426, 23=6331, 22=4068, 25=13673, 24=9564, 27=25136, 26=19044, 29=40683, 28=32686, 31=56406, 30=48458, 34=71215, 35=72174, 32=62624, 33=68027, 38=63230, 39=56008, 36=71738, 37=68577, 42=32636, 43=25318, 40=48676, 41=40362, 46=9627, 47=6329, 44=19086, 45=13701, 51=772, 50=1383, 49=2416, 48=3996, 55=31, 54=86, 53=150, 52=406, 59=1, 57=2, 56=7}
Related
public class Question2 {
//running time of function is N!!!!!!
public static boolean isThere(int[] array, int num, int index){
boolean isItThere = false; //running time of 1
for(int i =0; i <= index; i++){ //running time i
if(array[i] == num){ //running time of 1
isItThere = true; //running time of 1
}
}
return isItThere;
}
public static int[] firstAlgo(int N){
Random random = new Random(); //running time of 1(initilizing)k
int[] arr = new int[N];
for (int i = 0; i < N; i++){
int temp = random.nextInt(N+1); //running time of random is O(1)
while (isThere(arr, temp, i)){
temp = random.nextInt(N+1);
}
arr[i] = temp;
}
return arr;
}
}
I want to figure out the time complexity of the while loop, I know the running time of the isThere function is N and So is the main for loop in firstAlgo
The short version:
The expected runtime is Θ(N2 log N).
I have the math to back this up, as well as empirical data.
Here's a plot comparing the empirical amount of work done to the best-fit approximation I got for a function of the form N2 log N, which was (N2 ln N) / 1.06:
Curious? Keep reading. :-)
Let's take a step back from the code here and see what the actual logic is doing. The code works as follows: for each prefix of the array 0, 1, 2, 3, ..., N, the code continuously generates random numbers between 0 and N until it generates one that hasn't been seen before. It then writes down that number in the current array slot and moves on to the next one.
A few observations that we'll need in this analysis. Imagine that we're about to enter the kth iteration of the loop in firstAlgo. What can we say about the elements of the first k slots of the array? Well, we know the following:
The elements at positions 0, 1, 2, 3, ..., k-1 are all different from one another. The reason for this is that each loop iteration only stops once it's found something that doesn't otherwise appear in the array up to that point.
None of those values are equal to 0, because the array is initially populated with 0s and if 0 is generated in a previous step it won't be allowed.
As a consequence of (1) and (2), the elements in slots 0, 1, 2, ..., k-1, and k are all different.
Now, we can get down to some math. Let's look at iteration k of the loop in firstAlgo. Each time we generate a random number, we look at (k+1) array slots to make sure the number doesn't appear there. (I'm going to use this quantity, by the way, as a proxy for the total work done, since most of the energy will be spent scanning that array.) So then we need to ask - on expectation, how many numbers are we going to generate before we find a unique one?
Fact (3) from the above list is helpful here. It says that on iteration k, the first k+1 slots in the array are different from one another, and we need to generate a number different from all of those. There are N+1 options of random numbers we can pick, so there are (N+1) - (k+1) = N - k options for numbers we could pick that won't have been used. This means that the probability that we pick a number that hasn't yet come up is (N - k) / (N + 1).
A quick check to make sure this formula is right: when k = 0, we are okay generating any random number other than 0. There are N+1 choices, so the probability we do this is N / (N+1). That matches our formula from above. When k = N-1, then all previous array elements are different and there's only one number we can pick that will work. That means we have a success probability of 1 / (N+1), again matching our formula. Cool!
There's a useful fact from probability theory that if you keep flipping a biased coin that has probability p of coming up heads, the expected number of flips before you flip heads is 1 / p. (More formally, that's the mean of a geometric random variable with success probability p, and you can prove this using the formal definition of expected values and some Taylor series.) This means that on the kth iteration of that loop, the expected number of random numbers we'll need to generate before we get a unique one is (N + 1) / (N - k).
Overall, this means that the expected amount of work done on iteration k of the loop in firstAlgo is given by (N + 1)(k + 1) / (N - k). That's because
there are, on expectation, (N + 1)/(N - k) numbers generated, and
each generated number requires (k + 1) array slots to be checked.
We can then get our total amount of work done by summing this up from k = 0 to N - 1. That gives us
0+1 1+1 2+1 N
(N+1)----- + (N+1)----- + (N+1)----- + ... + (N+1)-----
N-0 N-1 N-2 1
Now, "all" we have to do is simplify this summation to see what we get back. :-)
Let's begin by factoring out the common (N + 1) term here, giving back
/ 1 2 3 N \
(N+1)| --- + --- + --- + ... + --- |
\ N N-1 N-2 1 /
Ignoring that (N + 1) term, we're left with the task of simplifying the sum
1 2 3 N
--- + --- + --- + ... + ---
N N-1 N-2 1
Now, how do we evaluate this sum? Here's a helpful fact. The sum
1 1 1 1
--- + --- + --- + ... + ---
N N-1 N-2 1
gives back the Nth harmonic number (denoted HN) and is Θ(log N). More than just being Θ(log N), it's very, very close to ln N.
With that in mind, we can do this rewrite:
1 2 3 N
--- + --- + --- + ... + ---
N N-1 N-2 1
1 1 1 1
= --- + --- + --- + ... + ---
N N-1 N-2 1
1 1 1
+ --- + --- + ... + ---
N-1 N-2 1
1 1
+ --- + ... + ---
N-2 1
+ ...
1
+ ---
1
The basic idea here is to treat (k + 1) / N as (k + 1) copies of the fraction 1 / N, and then to regroup them into separate rows like this.
Once we've done this, notice that the top row is the Nth harmonic number Hn, and the item below that is the (N - 1)st harmonic number Hn-1, and the item below that is the (N - 2)nd harmonic number Hn - 2, etc. So this means that our fraction sum works out to
H1 + H2 + H3 + ... + HN
= Θ(log 1 + log 2 + log 3 + ... + log N)
= Θ(log N!) (properties of logs)
= Θ(N log N) (Stirling's approximation).
Multiplying this in by the original factor of N that we pulled out earlier, we get that the overall runtime is Θ(N2 log N).
So, does that hold up in practice? To find out, I ran the code over a range of inputs and counted the average number of iterations of the loop in isThere. I then divided each term by log N and did a polynomial-fit regression to see how closely the remainder matched Θ(N2). The regression found that the best polynomial plot had a polynomial term of N2.01, strongly supporting that (after multiplying back in the log N term) we're looking at N2 log N. (Note that running the same regression but without first dividing out the log N term shows a fit of N2.15, which clearly indicates something other than N2 is going on here.)
Using the the equation Predicted(N) = (N2 ln N) / 1.06, with that last constant determined empirically, we get the plot up above, which is almost a perfect match.
Now, a quick coda to this problem. In retrospect, I should have predicted that the runtime would be Θ(N2 log N) almost immediately. The reason why is that this problem is closely connected to the coupon collector's problem, a classic probability puzzle. In that puzzle, there are N different coupons to collect, and you keep collecting coupons at random. The question is then - on expectation, how many coupons do you need to collect before you'll have one copy of each of them?
That closely matches the problem we have here, where at each step we're picking from a bag of N+1 options and trying to eventually pick all of them. That would imply we need Θ(N log N) random draws across all iterations. However, the cost of each draw depends on which loop iteration we're in, with later draws costing more than earlier ones. Based on the assumption that most of the draws would be in the back half, I should have been able to guess that we'd do an average of Θ(N) work per draw, then multiplied that to get Θ(N2 log N). But instead, I just rederived things from scratch. Oops.
Phew, that was a trip! Hope this helps!
You can do it empirically. Just run the code, and time it, with different values of N. I did so, and the complexity seems to be O(N^2). I ran a bunch of tests, doubling N each time, and the times never seemed to back off from roughly quadrupling. I got tired of waiting at N = 256000, which took about 200K millis.
If you wanted to go this way, you'd want to do a more careful analysis with more runs. You could set up an outer loop to keep doing batches of tests, say 10 at each level, timing them, and then doubling N and doing it all again...recording all the times. You could run the test overnight and get a pretty clear idea of the behavior empirically.
If this isn't the way you want to go about this, it could at least be a way to double-check your answer.
Remember that Big-O is worst case behavior. As mentioned in one of the comments, this has a chance to never terminate leading to Big-O of infinite because this code is non-deterministic.
For an average case where random does what's expected. You are looking at O(N) for the isThere function. For that last iteration to find a value, you will average N operations. At this point you are up to O(N^2). Finally you need to repeat this operation N times to fill the array, which brings you to O(N^3).
I have a really simple question on arrays. I've been watching some tutorials and couldn't understand why the code below gives Frequency outputs as random combinations of 1**. It never gives numbers like 5, 67, 541 etc., it always gives something like 150, 175, 183 etc. I hope I made myself clear. Thanks alot!
Code:
Random rand = new Random();
int freq[] = new int[7];
for(int roll=1; roll<=1000; roll++){
++freq[1+rand.nextInt(6)];
}
System.out.println("Face\tFrequency");
for(int face=1; face<freq.length; face++){
System.out.println(face + "\t" + freq[face]);
}
Sample Output:
Face Frequency
1 176
2 171
3 157
4 159
5 164
6 173
This is actually more of a math question than a programming question!
There are six possible outcomes for the dice rolls and you roll the dice 1,000 times. That means that if you wanted to see a number that isn't of the form "one hundred and X," you'd need to either see 200 or more of one number or 99 or fewer of a number. The expected number of times you'll see each number is 1000 / 6 = 166.7, so in order to see 200 or more of a number you'd need to deviate by +33.3 or -66.7 from the true value. This can happen; it's just uncommon.
I wrote a program that simulated rolling dice like this until you got one of these types of rolls and counted how many times you'd need to roll the dice. After doing this 1,000 times, I found that on average, you need to roll the dice 53 times before you're going to see a number that isn't in the one hundreds. Here's the code:
import java.util.*;
public class DiceRolls {
/* The number of trials to run. */
private static final int NUM_TRIALS = 1000;
public static void main(String[] args) {
Random rand = new Random();
int totalRuns = 0;
for (int i = 0; i < NUM_TRIALS; i++) {
totalRuns += runsUntilEvent(rand);
}
System.out.println(totalRuns / (double)NUM_TRIALS);
}
private static int runsUntilEvent(Random rand) {
/* Track how many tries we needed. */
int numTries = 0;
while (true) {
numTries++;
/* Rather than indexing from 1 to 6, I'm indexing from 0 to 5. */
int freq[] = new int[6];
for(int roll = 0; roll < 1000; roll++){
++freq[rand.nextInt(6)];
}
/* See if this isn't in the hundreds. */
for(int face = 0; face < freq.length; face++){
if (freq[face] >= 200 || freq[face] <= 99) {
return numTries;
}
}
}
}
}
So the answer to your question is "you may see it, but it's unlikely and you'd have to run the program a lot to see it."
Hope this helps!
This is normal. A frequency of 5 would be very unusual because it would mean that number was only rolled 5 times out of 1000. The numbers you are getting are about 1/6 of the total 1000 rolls, so each of the six numbers are being rolled about one sixth of the time.
Due to the fact that you loop 1000 times the deviation is quite small.
If you loop only 10 times, but increase the numbers by 100 in the loop, you get a higher deviation.
The output is the number of the times a number is found in each "dice roll". As far as you are using pseudo-random number generation, the number generation is considered as "fair", therefore you have a "fair dice roll", meaning that on average, every number has equal chances on each roll to appear.
Therefore, each number should have appear on average almost 1/6 times of the total rolls, which are the results you now have.
If you increase the number of the total rolls, the deviation of 1/6 between each number will be greater and increasing in analogy with the number of rolls.
I guess your original question is why you are not getting low values like below 100 (less than 10%) or greater than 500 (greater than 50%).
Short answer, you were not lucky enough. Because the way you created your random generator, you have to run your program many more times to observe these values.
Let's calculate probability of any outcome (frequency) to occur in more than 50% cases if you run your program just once:
This can be easily estimated if we roll by pairs (e.g. 1000 rolls is 500 pairs)
For each pair, probability of some specific side (e.g. 6) is a number of qualified outcomes divided by total number of outcomes:
Qualified outcomes (for side 6 to occur in 50% rolls or more):
16, 26, 36, 46, 56, 66,
61, 62, 63, 64, 65
Total outcomes: 6^2 = 36
So 11 qualified outcomes divided by total gives probability of one side to occur 50% time or more, which is 11/36
Now we need this to happen N/2 times in a row, so we should multiply the pair probability N/2 times: (11/36)^(N/2)
Wolfram alpha gives us 3.5 * 10^-258 which means you need to run your program 2.85 * 10^257 times to expect (once) a frequency of one specific side 500 times or more.
Divide that by 6 to see this for any side.
I have this assignment in school; we are to write a simple program/method/algorithm in java that has us take two numerical inputs and output count of all the numbers in the range between two inputs which are dividable by 2 or 3 or 5.
The assignment is fairly simple, since you can just iterate through all the numbers in range and increment the counter whenever all the criteria is met.
But we also get 10 test inputs and a timer that evaluates the efficiency of our algorithm. First eight fell through, since the eight values were < 10^6. But the last two test input vales were < 10^18 and my algorithm failed.
So I started thinking in the direction of prime number counting function and sieve Eratosthenes, but my head started to hurt. Any ideas on a faster but still simple enough algorithm?
I came up with something like that
public static void main(String[] args) {
long a = 123456789012345678L, b = 876543210987654321L;
long score = getCount(b) - getCount(a - 1);
System.out.println("Divisible by 2 or 3 or 5: " + score);
}
public static long getCount(long end) {
return (end / 2) + (end / 3) + (end / 5) - ((end / 6) + (end / 10) + (end / 15)) + (end / 30);
}
The solution:
It counts how many numbers are divisible by 2 or 3 or 5 separately and sums that.
Now we need to discard numbers that where counted twice: for 2 and 3 it will be every 6th number, for 2 and 5 every 10th number, for 3 and 5 every 15th number
At the end we need to include numbers that are divisible by 2 and 3 and 5 that where discarded in step 2 so we add every 30th number.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
Here's the problem: https://projecteuler.net/problem=15
I've come up with a pattern which I thought would work for this and I've looked at what other people have done and they've done the same thing, such as here: http://code.jasonbhill.com/python/project-euler-problem-15/ But I always get a different answer. Here's my code.
import java.util.*;
public class Problem15 {
public static void main(String[] args) {
ArrayList<Long> list = new ArrayList<Long>();
list.add((long)1);
int size;
for (int x = 1;x<20;x++){
size = list.size();
for(int y = 1;y<size;y++){
long sum = list.get(y-1)+list.get(y);
list.set(y, sum);
}
list.add(list.get(size-1)*2);
System.out.println(list);
}
}
}
edit:
In response to Edward, I think my method is currently what you said before your edit in that this isn't about brute force but I'm just summing the possible ways from each point in the grid. However, I don't need a 2d array to do this because I'm only looking at possible moves from only the side. Here's something I drew up to hopefully explain my process.
So for a 1x1. Like you said, once you reach the limit of one direction, you can only travel in the limit of the other, so there's 2 ways. This isn't particularly helpful for a 1x1 but it is for larger ones. For a 2x2, you know that the top corner, being the limit of right, only has 1 possible path from that point. The same logic applies to the bottom corner. And, because you have a square which you have already solved for, a 1x1, you know that the middle point has 2 paths from there. Now, if you look at the sides, you see that the point for instance that has 2 beneath it and 1 to the right has the sum of the number of paths in those adjacent points so then that point must have 3 paths. Same for the other side, giving the top left corner the sum of 3 and 3, or 2 times 3.
Now if you look at my code, that's what it's trying to do. The element with index 0 is always 1, and for the rest of the array, it adds together the previous term and itself and replaces the current term. Lastly, to find the total number of paths, it just doubles the last number. So if the program were to try and solve for a 4x4, the array would currently look like {1, 4, 10, 20}. So the program would change it to {1, 5, 10, 20}, then {1, 5, 15, 20}, then {1, 5, 15, 35}, and finally, adds the total number of paths, {1, 5, 15, 35, 70}. I think this is what you were trying to explain to me in your answer however my answer always comes out incorrect.
Realize that it's more about mathematical complexity than brute force searching.
You have a two dimensional array of points, where you can chose to only travel away from the origin in the x or the y direction. As such, you can represent your travel like so:
(0, 0), (1, 0), (1, 1), (2, 1), (2, 2)
some things become immediately obvious. The first one is that any path through the mess is going to require x + y steps, travelling through x + y + 1 locations. It is a feature of a Manhattan distance style path.
The second is that at any one point until you hit the maximum x or y, you can select either of the two options (x or y); but, as soon as one or the other is at it's limit, the only option left is to chose the non-maximum value repeatedly until it also becomes a maximum.
With this you might have enough of a hint to solve the math problem. Then you won't even need to search through the different paths to get an algorithm that can solve the problem.
--- edited to give a bit more of a hint ---
Each two dimensional array of paths can be broken down into smaller two dimensional arrays of paths. So the solution to f(3, 5) where the function f yields the number of paths is equal to f(2, 5) + f(3, 4). Note that f(0, 5) directly equals 1, as does f(3, 0) because you no longer have "choices" when the paths are forced to be linear.
Once you model the function, you don't even need the array to walk the paths....
f(1, 1) = f(0, 1) + f(1, 0)
f(0, 1) = 1
f(1, 0) = 1
f(1, 1) = 1 + 1
f(1, 1) = 2
and for a set of 3 x 3 verticies (like the example cited has)
f(2, 2) = f(1, 2) + f(2, 1)
f(1, 2) = f(0, 1) + f(1, 1)
(from before)
f(1, 1) = 2
f(0, 2) = 1
f(1, 2) = 2 + 1 = 3
likewise (because it's the mirror image)
f(2, 1) = 1 + 2 = 3
so
f(2, 2) = 3 + 3 = 6
--- last edit (I hope!) ---
Ok, so now you may get the idea that you have really two choices (go down) or (go right). Consider a bag containing four "commands", 2 of "go down" and 2 of "go right". How many different ways can you select the commands from the bag?
Such a "selection" is a permutation, but since we are selecting all of them, it is a special type of permutation called an "order" or "ordering".
The number of binomial (one or the other) orderings is ruled by the mathematical formula
number of orderings = (A + B)!/(A! * B!)
where A is the "count" of items of type A, and B is the "count" of items of type B
3x3 vertices, 2 down choices, 2 right choices
number of orderings = (2+2)!/2!*2!
4!/1*2*1*2
1*2*3*4/1*2*1*2
(1*2)*3*4/(1*2)*1*2
3*4/2
12/2
6
You could probably do a 20*20 by hand if you needed, but the factorial formula is simple enough to do by computer (although keep an eye you don't ruin the answer with an integer overflow).
Another implementation:
public static void main(String[] args) {
int n = 20;
long matrix[][] = new long[n][n];
for (int i = 0; i < n; i++) {
matrix[i][0] = i + 2;
matrix[0][i] = i + 2;
}
for (int i = 1; i < n; i++) {
for (int j = i; j < n; j++) { // j>=i
matrix[i][j] = matrix[i - 1][j] + matrix[i][j - 1];
matrix[j][i] = matrix[i][j]; // avoids double computation (difference)
}
}
System.out.println(matrix[n - 1][n - 1]);
}
Time: 43 microseconds (without printing)
It is based on the following matrix:
| 1 2 3 4 ...
---------------------
1 | 2 3 4 5 ...
2 | 3 6 10 15
3 | 4 10 20 35
4 | 5 15 35 70
. | .
. | .
. | .
where
6 = 3 + 3
10 = 6 + 4
15 = 10 + 5
...
70 = 35 + 35
Notice that I used i + 2 instead of i + 1 in the implementation because the first index is 0.
Of course, the fastest solution is to use a mathematical formula (see Edwin's post) and the code for it:
public static void main(String[] args) {
int n = 20;
long result = 1;
for ( int i = 1 ; i <= n ; i++ ) {
result *= (i+n);
result /= i;
}
System.out.println(result);
}
takes only 5 microseconds (without printing).
If you are afraid about the loss of precision, notice that the product of n consecutive numbers is divisible by n!.
To have a better understanding why the formula is:
(d+r)!
F = --------- , where |D| = d and |R| = r
d!*r!
instead of F = (d+r)!, imagine that every "down" and "right" has an index:
down1,right1,right2,down2,down3,right3
The second formula counts all possible permutations for the "commands" above, but in our case there is no difference between down1, down2 and down3. So, the second formula will count 6 (3!) times the same thing:
down1,down2,down3
down1,down3,down2
down2,down1,down3
down2,down3,down1
down3,down1,down2
down3,down2,down1
This is why we divide the (d+r)! by d!. Analogue for r!.
I was going through some coding exercises, and had some trouble with this question:
From 5 dice (6-sided) rolls, generate a random number in the range [1 - 100].
I implemented the following method, but the returned number is not random (called the function 1,000,000 times and several numbers never show up in 1 - 100).
public static int generator() {
Random rand = new Random();
int dices = 0;
for(int i = 0; i < 5; i++) {
dices += rand.nextInt(6) + 1;
}
int originalStart = 5;
int originalEnd = 30;
int newStart = 1;
int newEnd = 100;
double scale = (double) (newEnd - newStart) / (originalEnd - originalStart);
return (int) (newStart + ((dices - originalStart) * scale));
}
Ok, so 5 dice rolls, each with 6 options. if they are un-ordered you have a range of 5-30 as mentioned above - never sufficient for 1-100.
You need to assume an order, this gives you a scale of 1,1,1,1,1 - 6,6,6,6,6 (base 6) assuming 1 --> 0 value, you have a 5 digit base 6 number generated. As we all know 6^5 = 7776 unique possibilities. ;)
For this I am going to give you a biased random solution.
int total = 0;
int[] diceRolls;
for (int roll : diceRolls) {
total = total*6 + roll - 1;
}
return total % 100 + 1;
thanks to JosEdu for clarifying bracket requirement
Also if you wanted to un-bias this, you could divide range by the maxval given in my description above, and subsequently multiply by your total (then add offset), but you would still need to determine what rounding rules you used.
Rolling a 6 sided die 5 times results in 6^5 = 7776 possible sequences, all equally probable. Ideally you'd want to partition those sequences into 100 groups of equal size and you'd have your [1 - 100] rng, but since 7776 isn't evenly divisible by 100 this isn't possible. The best you can do to minimize the bias is 76 groups mapped to by 78 sequences each and 24 groups mapped to by 77 sequences each. Encode the (ordered) dice rolls as a base 6 number n, and return 1 + (n % 100).
Not only is there no way to remove the bias with 5 dice rolls, there is no number of dice rolls that will remove the bias entirely. There is no value of k for which 6^k is evenly divisible by 100 (consider the prime factorizations). That doesn't mean there's no way to remove the bias, it just means you can't remove the bias using a procedure that is guaranteed to terminate after any specific number of dice rolls. But you could for example do 3 dice rolls producing 6^3 = 216 sequences encoded as the base 6 number n, and return 1 + (n % 100) if n < 200. The catch is that if n >= 200 you have to repeat the procedure, and keep repeating until you get n < 200. That way there's no bias but there's also no limit to how long you might be stuck in the loop. But since the probability of having to repeat is only 16/216 each time, from a practical standpoint it's not really much of a problem.
The problem is there aren't enough random values in 5-30 to map one to one to 1-100 interval. This means certain values are destined to never show up; the amount of these "lost" values depends on the size ratio of the two intervals.
You can leverage the power of your dice in a way more efficient way, however. Here's how I'd do it:
Approach 1
Use the result of the first dice to choose one subinterval from the
6 equal subintervals with size 16.5 (99/6).
Use the result of the second dice to choose one subinterval from the 6 equal sub-subintervals of the subinterval you chose in step one.
Use... I guess you know what follows next.
Approach 2
Construct your random number using digits in a base-6 system. I.E. The first dice will be the first digit of the base-6 number, the second dice - the second digit, etc.
Then convert to base-10, and divide by (46656/99). You should have your random number. You could in fact only use 3 dice, of course, the rest two are just redundant.