Project Euler 31: Why does this solution work? - algorithm

I have been studying various solution to Project Euler question #31:
In England the currency is made up of pound, £, and pence, p, and
there are eight coins in general circulation: 1p, 2p, 5p, 10p, 20p,
50p, £1 (100p) and £2 (200p).
It is possible to make £2 in the following way: 1x£1 + 1x50p + 2x20p + 1x5p + 1x2p + 3x1p
How many different ways can £2 be made using any number of coins?
I am confused why this particular brute force solution works (source):
int target = 200;
int ways = 0;
for (int a = target; a >= 0; a -= 200) {
for (int b = a; b >= 0; b -= 100) {
for (int c = b; c >= 0; c -= 50) {
for (int d = c; d >= 0; d -= 20) {
for (int e = d; e >= 0; e -= 10) {
for (int f = e; f >= 0; f -= 5) {
for (int g = f; g >= 0; g -= 2) {
ways++;
}
}
}
}
}
}
}
Based on this solution I would expect there to be one more nested for-loop at the innermost level that is decrementing by 1, for coins of 1p value.
For example, when do we count the scenario where 200p is made up of only 1p coins?
As it stands currently, the code leads to the correct answer. But if an additional for-loop was added then it would seem the answer should be much bigger. What am I missing?

Because the for-loops count up ways to make a number <= 200, assuming the remainder is made up of 1p coins. For example, consider the first iteration, where a = b = c = d = e = f = g = 200, this is exactly the case you asked about explicitly, where 200p is made of all 1's.

For any given combination of 2p, 5p, 10p, 20p, 50p, £1 (100p) and £2 (200p) coins that sum up to £2 or less there's precisely one number of 1p coins that would make up an even £2. Hence, you don't need another loop to iterate the number of 1p coins.

Related

Count the number of ways a given sequence of digits can be split with each part at most K

Hi guys I'm practicing dynamic programming and came across the following problem:
Given a number K, 0 <= K <= 10^100, a sequence of digits N, what is the number of possible ways of dividing N so that each part is at most K?
Input:
K = 8
N = 123
Output: 1
Explanation:
123
1-23
12-3
1-2-3
Are all possibilities of spliting N and only the last one is valid...
What I have achieved so far:
Let Dp[i] = the number of valid ways of dividing N, using i first digits.
Given a state, i must use the previous answer to compute new answers, we have 2 possibilities:
Use dp[i-1] + number of valid ways that split the digit i
Use dp[i-1] + number of valid ways that not split the digit i
But I'm stuck there and I don't know what to do
Thanks
Using dynamic programming implies that you need to think about the problem in terms of subproblems.
Let's denote by N[i...] the suffix of N starting at index i (for instance, with N = 45678955, we have N[3...] = 78955)
Let's denote by dp[i] the number of possible ways of dividing N[i...] so that each part is at most K.
We will also use a small function, max_part_len(N, K, i) which will represent the maximum length of a 'part' starting at i. For instance, with N = 45678955, K = 37, i = 3, we have max_part_len(N, K, i) = 1 because 7 < 37 but 78 > 37.
Now we can write the recurrence (or induction) relation on dp[i].
dp[i] = sum_(j from 1 to max_part_len(N, K, i)) dp[i+j]
This relation means that the the number of possible ways of dividing N[i...] so that each part is at most K, is:
The sum of the the number of possible ways of dividing N[i+j...] so that each part is at most K, for each j such that N[i...j] <= k.
From there the algorithm is quite straight forward if you understood the basics of dynamic programming, I leave this part to you ;-)
I think we can also use divide and conquer. Let f(l, r) represent the number of ways to divide the range of digits indexed from l to r, so that each part is at most k. Then divide the string, 45678955 in two:
4567 8955
and the result would be
f(4567) * f(8955)
plus a division with a part that includes at least one from each side of the split, so each left extension paired with all right extensions. Say k was 1000. Then
f(456) * 1 * f(955) + // 78
f(456) * 1 * f(55) + // 789
f(45) * 1 * f(955) // 678
where each one of the calls to f performs a similar divide and conquer.
Here's JavaScript code comparing a recursive (top-down) implementation of m.raynal's algorithm with this divide and conquer:
function max_part_len(N, K, i){
let d = 0;
let a = 0;
while (a <= K && d <= N.length - i){
d = d + 1;
a = Number(N.substr(i, d));
}
return d - 1;
}
// m.raynal's algorithm
function f(N, K, i, memo={}){
let key = String([N, i])
if (memo.hasOwnProperty(key))
return memo[key];
if (i == N.length)
return 1
if (i == N.length - 1)
return (Number(N[i]) <= K) & 1
let s = 0;
for (let j=1; j<=max_part_len(N, K, i); j++)
s = s + f(N, K, i + j, memo);
return memo[key] = s;
}
// divide and conquer
function g(N, K, memo={}){
if (memo.hasOwnProperty(N))
return memo[N];
if (!N)
return memo[N] = 1;
if (N.length == 1)
return memo[N] = (Number(N) <= K) & 1;
let mid = Math.floor(N.length / 2);
let left = g(N.substr(0, mid), K);
let right = g(N.substr(mid), K);
let s = 0;
let i = mid - 1;
let j = mid;
let str = N.substring(i, j + 1);
while (i >= 0 && Number(str) <= K){
if (j == N.length){
if (i == 0){
break;
} else{
i = i - 1;
j = mid;
str = N.substring(i, j + 1);
continue
}
}
let l = g(N.substring(0, i), K, memo);
let r = g(N.substring(j + 1, N.length, memo), K);
s = s + l * r;
j = j + 1;
str = N.substring(i, j + 1);
if (Number(str) > K){
j = mid;
i = i - 1;
str = N.substring(i, j + 1);
}
}
return memo[N] = left * right + s;
}
let start = new Date;
for (let i=5; i<100000; i++){
let k = Math.ceil(Math.random() * i)
let ii = String(i);
let ff = f(ii, k, 0);
}
console.log(`Running f() 100,000 times took ${ (new Date - start)/1000 } sec`)
start = new Date;
for (let i=5; i<100000; i++){
let k = Math.ceil(Math.random() * i)
let ii = String(i);
let gg = g(ii, k);
}
console.log(`Running g() 100,000 times took ${ (new Date - start)/1000 } sec`)
start = new Date;
for (let i=5; i<100000; i++){
let k = Math.ceil(Math.random() * i)
let ii = String(i);
let ff = f(ii, k, 0);
let gg = g(ii, k);
if (ff != gg){
console.log("Mismatch found.", ii, k, ff, gg);
break;
}
}
console.log(`No discrepancies found between f() and g(). ${ (new Date - start)/1000 } sec`)

Smallest monetary amount that can be obtained using only coins of specified denominations that exceeds a threshold

In other words, given a set of n positive integers A and a threshold B, I want to find the smallest C so that:
C > B
C = A[1] * k[1] + A[2] * k[2] + ... + A[n] * k[n], k[i] are integers >= 0
As an example if A = { 6, 11, 16 } then the values we can obtain are: { 0, 6, 11, 12, 16, 17, 18, 22, 23, 24, 27, 28, 29, 30, 32 ... } so if B = 14 then C would be 16, B = 22 => C = 23, B = 18 => C = 22
This problem was given these constraints: 2 < n < 5000 0 < A[i] < 50000 and 1 < B < 10^9 ( this is why I got stuck ). Also you had to calculate for an array B of size < 1000 an array C (but this may not matter). And the algorithm should run in under 0.3 sec in C++.
An algorithm like the one described here solves it but it is not fast enough: https://www.geeksforgeeks.org/dynamic-programming-set-7-coin-change/
I calculate table until B + Amin because Amin * k <= B <= Amin * ( k + 1 ) <= B + Amin
Here's the algorithm (in pseudo C++):
int n, A[n], B;
int Amin; // smallest number from A
// table[i] will tell us if it is possible or not to obtain the number i
bool table[B + Amin];
table[0] = true;
for( int i = 0; i < n; ++i )
{
int x = A[i]; // current number / denomination
for( int j = x; j <= B + Amin; ++j )
if( table[j - x] )
table[j] = true;
}
// now we can do something like this:
int result = B + 1;
while( !table[result] )
++result;
This algorithm has a complexity of O(n*B) and I'm looking for something that is independent of B ( or maybe has O(log(B)) or O(sqrt(B)) )
Note: if we make the first requirement C >= B then the problem doesn't change ( just add +1 to B ) and we can ask it like this: If we have specific coins or banknotes ( infinite of them ) and want to purchase something with them, then what is the amount we can pay so that the cashier has to give back minimal change.
Things that I suspect may help:
https://en.wikipedia.org/wiki/Coin_problem
If Greatest Common Divisor ( x, y ) = 1 then anything higher than xy − x − y can be obtained using x and y.
https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm
https://en.wikipedia.org/wiki/Subset_sum_problem
Edit: added example and note.
I don't think you can get better than O(n*B), because the Frobenius number (above this number all amounts can be built with the given denominations) of 49999 and 49998 is 2499750005 a lot bigger than 10^9 and you need to calculate the best value at least for some inputs. If gcd(A) > 1 then the Frobenius number doesn't exist, but this could be prevented by dividing all A and B (round down) by gcd(A) and multiply the C you get by gcd(A) to get the final result.
There is still a lot of room for improvements in your pseudo code. You look at all denominations almost B+Amin times and also set the value in the table to true multiple times.
The standard implementation would look something like this:
sort(A);
table[0] = true;
for (int i = A[0]; i <= B + A[0]; i++)
for (int j = 0; j < n && A[j] <= i; j++)
if (table[i - A[j]]) {
table[i] = true;
break;
}
This is already a little better (note the break). I call it the backwards implementation, because you look back from all positions in the table to see if you can find a value which has the difference of one of the given denominations. You could also introduce a counter for the number of consecutive values set to true in the table (increase the counter when you set a value in the table to true, reset if the value couldn't be built, return B+1 if counter == A[0] - 1).
Maybe you could even get better results with a forward implementation, because the table can be very sparse, here table values which are false are skipped instead of denominations:
table[0] = true;
for (int i = 0; i <= B + Amin; i++)
if (table[i])
for (j = 0; j < n; j++)
if (i + A[j] <= B + Amin)
table[i + A[j]] = true;

Get three-digit numbers where the sum of factorials of the digits equals to the number

I am reading combinatorial book for school and have to do some exercises, this is one of them:
Write a computer program to determine whether there is a three-digit integer abc (= 100a + 10b + c) where abc = a! + b! + c!.
My attempt is below.
Can someone propose a better (faster) algorithm for this task?
int power(int n)
{
int sum=1;
for(int i=2;i<=n;i++){
sum*=i;
}
return sum;
}
void compute()
{
int abc;
int powa,powb,powc;
for(int a=1;a<100;a++){
for(int b=1;b<100;b++){
for(int c=1;c<100;c++){
abc=(100*a)+(10*b)+c;
powa=power(a);
powb=power(b);
powc=power(c);
if(abc==(powa+powb+powc)){
cout<<"There is ,nums are :"<<a<<","<<b<<","<<c<<endl;
}
}
}
}
}
int main()
{
compute();
return 0;
}
for (var n = 0; n < 1000; n++)
{
var a = (n / 100);
var b = (n / 10) % 10;
var c = n % 10;
var m = a! + b! + c!;
if (m == n)
{
Console.WriteLine(n);
}
}
Maybe start with 100 if you don't want to consider leading zeros. Or this way.
for (var a = 0; a < 10; a++)
{
for (var b = 0; b < 10; b++)
{
for (var c = 0; c < 10; c++)
{
var n = 100 * a + 10 * b + c;
var m = a! + b! + c!;
if (m == n)
{
Console.WriteLine(n);
}
}
}
}
Again maybe skip zeros if you don't want leading zeros. You may also be able to exit loops early if you determine that m will always be larger than n. For example a, b and c can not become larger than 6 because 7! is larger than 999.
We have 900 possible numbers at the beginning. Observing the rules we can notice the following:
7!, 8! and 9! are bigger than 999, so they cannot be used. This gets us down to 294 possible numbers.
0!, 1!, 2!, 3! and !4 are all smaller than 25. This means that at least one of the three digits you use will have to be five or bigger. This reduces it down to 194 possible numbers.
Now that we've eliminated a big number of cases on paper, we need to check them all. Instead of calculating the factorials all the time we calculate them once for every digit 0-6 and save them in an array.
Then for every combination three digits 0-6 (with the first one being 1) where at least one
is bigger than 4, we check if it fits the condition a!+b!+c!=a*100+b*10+c.
There might be some additional ways to optimize it, but even this is overkill considering you only have 3 digits (which is a very small number of possible cases).
int factorials[7];
int factorial(int n)
{
if(n<2)return n;
else return n*factorial(n-1);
}
void check(int a, int b, int c)
{
if((a>4 || b>4 || c>4) && factorials[a]+factorials[b]+factorials[c]==a*100+b*10+c)
cout<<"There is, nums are : "<<a<<","<<b<<","<<c<<endl;
}
int main()
{
for(int i=0;i<7;i++)factorials[i]=factorial(i);
for(int a=1;a<7;a++)
for(int b=0;b<7;b++)
for(int c=0;c<7;c++)check(a,b,c);
return 0;
}

Better algorithm for finding solutions to "a + b = c + d"

So lets abcd is a number between 1000 and 9999 and a,b,c,d are it's digits.
So find the number, where a+b=c+d. There is one solution down with four loops, but i need
a solution with three loops.
for (int a = 1; a <= 9; a++)
{
for (int b = 0; b <= 9; b++)
{
for (int c = 0; c <= 9; c++)
{
for (int d = 0; d <= 9; d++)
{
if ((a + b) == (c + d))
{
Console.WriteLine(" " + a + " " + b + " " + c + " " + d);
}
}
}
}
}
If someone asked you to solve the x + 1 == 2 equation, would you actually attempt to loop over all possible values of x to see which one fits? I hope not. You will probably figure out that the equation permits an immediate direct analytical solution x = 2 - 1 = 1.
The same logic applies to your case. Once you know a, b and c, your a + b == c + d equation permits a direct solution for d: d = a + b - c. There's no need to iterate over d just like there's no need to iterate over x in x + 1 == 2.
The first 3 loops establish values for a, b, and c. Knowing this and your equation just compute what d needs to be in order for your equation to hold true. Then check that the number computed for d is valid.
for (int a = 1; a <= 9; a++)
{
for (int b = 0; b <= 9; b++)
{
for (int c = 0; c <= 9; c++)
{
d = a + b - c;
if (d <= 9 && d >= 0)
{
Console.WriteLine(" " + a + " " + b + " " + c + " " + d);
}
}
}
}
What you have a special case of 4SUM that can be solved via one of the ways to quadratically solve 3SUM, making it O(N^2). Although it requires some use of data structures.
The first thing to note is since you are looking for A + B = C + D, What you really want to do is find a list of pairs of numbers that add up to some number S.
You can simply this by having a map/dict where the key is the sum and it maps to an list of pairs that equal that sum. The result is:
S = [(a,b),(c,d),(e,f),...] for a number of values of S
Which is equivalent to saying:
a + b = c + d = e + f = ... = S for a number of values of S
You then simply go through each sum and remove the ones where there is only a single element in the list.
I'm thinking you can then break go through the combinations of pairs obtaining things like a + b = c + d and c + d = e + f, at a cost of an additional O(N^2) as long as there are no duplicates due to limits on how many ways you can obtain a sum. Although, I might be wrong and it takes O(N^3) to list the solution in that form.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace test
{
class Program
{
static void Main(string[] args)
{
var number = Enumerable.Range(1000, 9000);
var list = from y in number
let a = y / 1000
let b = (y - 1000 * a) / 100
let c = (y - 1000 * a - 100 * b) / 10
let d = (y - 1000 * a - 100 * b - 10 * c)
where a + b == c + d
select y;
foreach (var item in list)
{
Console.WriteLine(item);
}
Console.Read();
}
}
}
String s1;
Set<String> s=new TreeSet();
for( int i=1;i<10;i++)
{
s1=null;
s1=""+i;
for(int j=0;j<10;j++)
{
String tempj=s1;
s1=s1+j;
for(int k=0;k<10;k++)
{
String tempk=s1;
s1=s1+k;
int temp=i+j-k;
s1=s1+temp;
if(temp<10)
s.add(s1);
s1=tempk;
}
s1=tempj;
}
}
for(String i:s )
System.out.println(i+);
----------------------------------------------------------------EDIT 1------------------
import java.util.*;
public class HelloWorld{
public static void main(String []args){
String happyNumber;
Set<String> numberSet=new TreeSet();
for( int i=1;i<10;i++)
{
happyNumber=null; //Every 1st loop will give a new number so old one has to be deleted
happyNumber=""+i; //Adding the number in 1st place (place value will be i*10000)
for(int j=0;j<10;j++) //Loop for putting nmber in 1000th place
{
String tempj=happyNumber; //taking happyNumber( only one digit fixed till now) into a temp variable to permutate on other places
happyNumber=happyNumber+j; //Attaching another number after this we have (AB)
for(int k=0;k<10;k++) //permutating with value of c and calculating d
{
String tempk=happyNumber;
happyNumber=happyNumber+k; //Attaching variable for 3rd place (c)
int temp=i+j-k; //calculating value of d
if(temp<10) //checking whether we have right value for d
{
happyNumber=happyNumber+temp;
numberSet.add(happyNumber);
}
happyNumber=tempk; //bringing number back to its previous state for more combination
}
happyNumber=tempj; //bringing number back to its previous state for more combination
}
}
for(String i:numberSet )
System.out.println(i);
}
}

Find all the quadruples [a, b, c, d] where a^3 + b^3 = c^3 + d^3 when 1 <= a, b, c or d <= 10000 [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
Looking for an algorithm or some coding hints to find the solutions for
a^3 + b^3 = c^3 + d^3, where a, b, c and d all are in the range [1 .. 10000]
It's an interview question.
I'm thinking priority queues to at least iterate for a and b values. Some hint will be great, will try to work through from there.
Using a hash map to store the (cube,(a,b)), you can iterate all possible pairs of integers, and output a solution once you have found that the required sum of cubes is already in the map.
pseudo code:
map <- empty hash_map<int,list<pair<int,int>>>
for each a in range(0,10^5):
for each b in range(a,10^5): //making sure each pair repeats only once
cube <- a^3 + b^3
if map.containsKey(cube):
for each element e in map.get(cube):
output e.first(), e.last(), a, b //one solution
else:
map.put(cube,new list<pair<int,int>>)
//for both cases, add the just found pair to the relevant list
map.get(cube).add(cube,new pair(a,b))
This solution is O(n^2) space(1) and O(n^2 + OUTPUT) time on average, where OUTPUT is the size of the output.
EDIT:
Required space is actually O(n^2 logn), where n is the range (10^5), because to represent 10^5 integers you need ceil(log_2(10^15)) = 50 bits. So, you actually need something like 500,000,000,000 bits (+ overhead for map and list) which is ~58.2 GB (+ overhead).
Since for most machines it is a bit too much - you might want to consider storing the data on disk, or if you have 64bits machine - just store in into "memory" and let the OS and virtual memory system do this as best as it can.
(1) As the edit clarifies, it is actually O(n^2log(n)) space, however if we take each integer storage as O(1) (which is usually the case) we get O(n^2) space. Same principle will apply for the time complexity, obviously.
Using a priority queue is almost certainly the simplest solution, and also the most practical one, since it's O(n) storage (with a log factor if you require bignums). Any solution which involves computing all possible sums and putting them in a map will require O(n^2) storage, which soon becomes impractical.
My naive, non-optimized implementation using a priority queue is O(n^2 log(n)) time. Even so, it took less than five seconds for n = 10000 and about 750 seconds for n = 100000, using a couple of megabytes of storage. It certainly could be improved.
The basic idea, as per your comment, is to initialize a priority queue with pairs (a, a+1) for a in the range [1, N), and then repeatedly increment the second value of the smallest (by sum of cubes) tuple until it reaches N. If at any time the smallest two elements in the queue are equal, you have a solution. (I could paste the code, but you only asked for a hint.)
Using Hashmap (O(n^2) solution):
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import static java.lang.Math.pow;
/**
* Created by Anup on 10-10-2016.
*/
class Pair {
int a;
int b;
Pair(int x, int y) {
a = x;
b = y;
}
}
public class FindCubePair {
public static void main(String[] args) {
HashMap<Long, ArrayList<Pair>> hashMap = new HashMap<>();
int n = 100000;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
long sum = (long) (pow(i, 3) + pow(j, 3));
if(hashMap.containsKey(sum)) {
List<Pair> list = hashMap.get(sum);
for(Pair p : list) {
System.out.println(i + " " + j + " " + p.a + " " + p.b);
}
} else {
ArrayList<Pair> list = new ArrayList<>();
hashMap.put(sum, list);
}
hashMap.get(sum).add(new Pair(i, j));
}
}
}
}
Unfortunately, the value of integers printed does not even reach 1000 on my computer due to resource limitation.
A quicker than trivial solution is as follows: You calculate all values that a^3 + b^3 can have, and store all possible values of a and b with it. This is done by looping through a and b, storing the results (a^3 + b^3) in a binary tree and having a list of values (a's and b's) associated to each result.
After this step, you need to traverse the list and for each value, choose every possible assignment for a,b,c,d.
I think this solution takes O(n^2 log n) time and O(n^2) space, but i might be missing something.
int Search(){
int MAX = 10000000;
for(int a = 0; a < MAX; a++){
int a3 = a * a * a;
if(a3 > MAX) break;
for(int b = a; b < MAX; b ++){
int b3 = b * b * b;
if(a3 + b3 > MAX)break;
for(int c = 0; c < a; c++){
int c3 = c*c*c;
int m = a3 - c3;
int d = b+1;
while(true){
int d3 = d * d * d;
if(d3-b3 <= m){
if((d3 - b3) == m){
count++;
PUSH_Modified(a3, b3, c3, b3, a, b, c, d);
}
d++;
continue;
}
else
break;
}
}
}
}
return 0;
}
Let's assume a solution:
a=A, b=B, c=C, and d=D.
Given any solution we can generate another 3 solutions
abcd
ABCD
ABDC
BACD
BADC
Actually, if A=B, or C=D, then we might only have 1 or 2 further solutions.
We can choose the solutions we look for first by ordering A <= B and C <= D. This will reduce the search space. We can generate the missed solutions from the found ones.
There will always be at least one solution, where A=C and B=D. What we're looking for is when A>C and B<D. This comes from the ordering: C can't be greater than A because, as we've chosen to only look at solutions where D>C, the cube sum would be too big.
We can calculate A^3 + B^3, put it in a map as the key, with a vector of pairs A,B as the value.
There will be (n^2)/2 values.
If there are already values in the vector they will all have lower A and they are the solutions we're looking for. We can output them immediately, along with their permutations.
I'm not sure about complexity.
One Solution - using concept of finding 2 sum in a sorted array. This is O(n3)
public static void pairSum() {
int SZ = 100;
long[] powArray = new long[SZ];
for(int i = 0; i< SZ; i++){
int v = i+1;
powArray[i] = v*v*v;
}
int countPairs = 0;
int N1 = 0, N2 = SZ-1, N3, N4;
while(N2 > 0) {
N1=0;
while(N2-N1 > 2) {
long ts = powArray[N1] + powArray[N2];
N3 = N1+1; N4 = N2-1;
while(N4 > N3) {
if(powArray[N4]+powArray[N3] < ts) {
N3++;
}else if(powArray[N4]+powArray[N3] > ts) {
N4--;
}else{
//System.out.println((N1+1)+" "+(N2+1)+" "+(N3+1)+" "+(N4+1)+" CUBE "+ts);
countPairs++;
break;
}
}
N1++;
}
N2--;
}
System.out.println("quadruplet pair count:"+countPairs);
}
Logic :
a^3 + b^3 = c^3 + d^3
Then, a^3+b^3-c*3-d^3 = 0
Try to solve this equation by putting all combination of values for a,b,c and d in range of [0 , 10^5].
If equation is solved print the values of a,b,c and d
public static void main(String[] args) {
//find all solutions of a^3 + b^3 = c^3 + d^3
double power = 3;
long counter = 0; // to count the number of solution sets obtained
int limit = 100000; // range from 0 to limit
//looping through every combination of a,b,c and d
for(int a = 0;a<=limit;a++)
{
for(int b = 0;b<=limit;b++)
{
for(int c = 0;c<=limit;c++)
{
for(int d = 0;d<=limit;d++)
{
// logic used : a^3 + b^3 = c^3 + d^3 can be written as a^3 + b^3 - c^3 - d^3 = 0
long result = (long)(Math.pow(a,power ) + Math.pow(b,power ) - Math.pow(c,power ) - Math.pow(d,power ));
if(result == 0 )
{
counter++; // to count the number of solutions
//printing the solution
System.out.println( "a = "+ a + " b = " + b + " c = " + c + " d = " + d);
}
}
}
}
}
//just to understand the change in number of solutions as limit and power changes
System.out.println("Number of Solutions =" + counter);
}
Starting with the brute force approach, its pretty obvious it will O(n^4) time to execute.
If space is not a constraint, we can go for combination of list and maps.
The code is self-explanatory, we are using a nested list to keep track of all entries for a particular sum (key in map).
The time complexity is thus reduced from O(n^4) to O(n^2)
public void printAllCubes() {
int n = 50;
Map<Integer, ArrayList<ArrayList>> resMap = new HashMap<Integer, ArrayList<ArrayList>>();
ArrayList pairs = new ArrayList<Integer>();
ArrayList allPairsList = new ArrayList<ArrayList>();
for (int c = 1; c < n; c++) {
for (int d = 1; d < n; d++) {
int res = (int) (Math.pow(c, 3) + Math.pow(d, 3));
pairs.add(c);
pairs.add(d);
if (resMap.get(res) == null) {
allPairsList = new ArrayList<ArrayList>();
} else {
allPairsList = resMap.get(res);
}
allPairsList.add(pairs);
resMap.put(res, allPairsList);
pairs = new ArrayList<Integer>();
}
}
for (int a = 1; a < n; a++) {
for (int b = 1; b < n; b++) {
int result = (int) (Math.pow(a, 3) + Math.pow(b, 3));
ArrayList<ArrayList> pairList = resMap.get(result);
for (List p : pairList) {
System.out.print(a + " " + b + " ");
for (Object num : p)
System.out.print(num + " ");
System.out.println();
}
}
}
}

Resources