Skip to content

Commit

Permalink
Merge pull request mingchuno#54 from jackhftang/master
Browse files Browse the repository at this point in the history
Add nodejs fast generative search algorithm
  • Loading branch information
gaplo917 committed Mar 28, 2016
2 parents 186e84e + 7380754 commit 524287e
Show file tree
Hide file tree
Showing 6 changed files with 648 additions and 165 deletions.
56 changes: 56 additions & 0 deletions Advance/nodejs-baseN-generative-search/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
## problem statement
- Question: find a,b,c,d such that
1. all with distinct digits in base-N.
2. all have W=floor((N-1)/4) number of digits
3. a-b=c+d=e=11...1 (W number of 1)
- for example,
for base 17, find a,b,c,d,e,..m,n,o,p,r such that
abcd-efgh=ijkl && ijkl+mnop=rrrrr
for base 21, find a,b,c,d,e...s,t,u such that
abcde-fghij=klmno && klmno+pqrst=uuuuuu

## idea
- generate possible value of c one by one at most B^W time.
- for each value of c, try to find the corresponding d.
if not found, try next c; if found, try next step
- build a set of digits for a and b. Then build a table for
the difference of digits. Construct values of a and b starting
from least significant digit, this allow pruning a lot
of useless permutation.

## trick
- Due to symmetry of c and d (c + d = d + c = e),
we can just scan for c < d and then swap c and d each
time and do the same things twice. This allows reuse of
used-table and skip ~45% top-level iteration.

## time complexity
- the time spent on diffSearch() is a bit hard to analysis
the worst case is O(2^B), but usually should be much
faster than that.
- Let the time complexity of diffSearch() be T.
The overall complexity is O( B^(W+1)*T )

## space complexity
- current version use ans[] to store solutions in order
not to do IO. Except that only use two array

## result
- test on macbook pro 2014
take <1 seconds for base 10 width 2 ( 5 solutions)
take <1 seconds for base 16 width 2 ( 1794 solutions)
take <1 seconds for base 22 width 2 ( 17312 solutions)
take <1 seconds for base 28 width 2 ( 73812 solutions)
take <1 seconds for base 34 width 2 ( 214033 solutions)

take <1 seconds for base 17 width 4 ( 1430 solutions)
take ~8 seconds for base 21 width 4 ( 1595019 solutions)
take ~12 seconds for base 25 width 4 ( 62811420 solutions)
take ~1.5 minutes for base 29 width 4 (796724927 solutions)

take <4 seconds for base 21 width 5 ( 96220 solutions)
take ~15 seconds for base 22 width 5 ( 1076604 solutions)
take ~40 seconds for base 23 width 5 ( 9686936 solutions)

take ~10 minutes for base 25 width 6 ( 9310592 solutions)

158 changes: 158 additions & 0 deletions Advance/nodejs-baseN-generative-search/abcdefghppp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
function abcdefghppp(base, width){
var i;

// EXP[i] = base^i
var EXP = new Array(width);
EXP[0] = 1;
for(i=1; i<width; i++) EXP[i] = base*EXP[i-1];

// value of e
var e = 1;
for(i=0; i<width; i++) e = base*e+1;

// minimum value of a,b,c,d in form like 2034..
var MIN;
if( width == 1 ) MIN = 0;
else {
MIN = 2*base;
for(i=2; i<width; i++) MIN = base*MIN + i + 1;
}

var ans = [];
var cnt = 0;
var used1 = new Array(base);
var used2 = new Array(base);

function print(a,b,c,d){
var sa = a.toString(base);
var sb = b.toString(base);
var sc = c.toString(base);
var sd = d.toString(base);
var se = (a-b+d).toString(base);
return [sa,'-',sb,'=',sc,', ',sc,'+',sd,'=',se].join('');
}


// used-tables helpers
function reset(used){
for(var i=0; i<used.length; i++) used[i] = false;
used[1] = true;
}
function checkWrite(used, a){
// mutable check
for(var i=width; i--;){
var t = a%base;
if( used[t] ) return false;
used[t] = true;
a = (a-t)/base;
}
return true;
}

// return true to break
function generate(w,c){
if( w === width ){
var d = e - c;
if( d < c ) return true; // due to symmetry of c and d
reset(used2);
if( checkWrite(used2, c) && checkWrite(used2, d) ) solveAB(c);
return false;
}

// left-most digit cannot start with zero and one is used
var start = w === 0 ? 2 : 0;
for(var i=start; i<base; i++){
if( used1[i] ) continue;
used1[i] = true;
if( generate(w+1, base*c+i) ) return true;
used1[i] = false;
}
return false;
}

function solveAB(c){
var i,t;
// possible digits in a and b
var vs = [];
for(i=0; i<used2.length; i++) if( !used2[i] ) vs.push(i);

// O(B)
// diff[0][i] = all possible pairs of digits [a,b] such that b-a = i
// diff[1][i] = all possible pairs of digits [a,b] such that 10+b-a= i
var diff = [new Array(base), new Array(base)];
for(i=0; i<2; i++) for(var j=0; j<base; j++) diff[i][j] = [];
for(i=0; i<vs.length; i++){
for(var k=i+1; k<vs.length; k++){
var v1 = vs[i], v2 = vs[k];
diff[0][v2-v1].push([v1,v2]);
diff[1][base+v1-v2].push([v2,v1]);
}
}

function diffSearch(i, borrow, a, b){
if( i === width ){
if( a > b && b >= MIN ){
if(cnt < 50) ans.push(print(a,b,c,e-c));
cnt++;
}
return;
}
if( sp[i] + borrow === base ) return; // this imply that a0 = b0 = 0, so not a solution
for(var j=0; j<2; j++){
var vec = diff[j][sp[i]+borrow];
for(var k=0; k<vec.length; k++) {
var a0 = vec[k][1], b0 = vec[k][0];
if( used2[a0] || used2[b0] ) continue;
used2[a0] = used2[b0] = true;
diffSearch(i+1, j, a+a0*EXP[i], b+b0*EXP[i]);
used2[a0] = used2[b0] = false;
}
}
}
var sp = new Array(width);
for(i=0, t=c; i<width; i++, t = Math.floor(t/base) ) sp[i] = t%base;
diffSearch(0, 0, 0, 0);
// due to symmetry of c and d
c = e - c;
for(i=0, t=c; i<width; i++, t = Math.floor(t/base) ) sp[i] = t%base;
diffSearch(0, 0, 0, 0);
}

reset(used1);
generate(0,0);

return [cnt, ans];
}

var s2 = '============================================================';
[16,22,28,34].forEach(function(base) {
var s = Date.now();
var x = abcdefghppp(base, 2);
var e = Date.now();
var cnt = x[0], ans = x[1];
s2 += `
Case base=${base}
${ans.sort().join('\n')}
time: ${((e - s) / 1000)}s
number of solutions for base-${base} : ${cnt}
============================================================`
});
require('fs').writeFileSync('output_w2.txt', s2);


var s4 = '============================================================';
[17,21,25,29].forEach(function(base) {
var s = Date.now();
var x = abcdefghppp(base, 4);
var e = Date.now();
var cnt = x[0], ans = x[1];
s4 += `
Case base=${base}
${ans.sort().join('\n')}
time: ${((e - s) / 1000)}s
number of solutions for base-${base} : ${cnt}
============================================================
`
});
require('fs').writeFileSync('output_w4.txt', s4);

Loading

0 comments on commit 524287e

Please sign in to comment.