Rare numbers: Difference between revisions

Content added Content deleted
m (Shyam Sunder Gupta's URL seemed to have changed it's spelling from xxxxxxxxx.htm --> xxxxxxxx.html)
(Added Wren)
Line 4,425: Line 4,425:
24: 441,054,594,034,340
24: 441,054,594,034,340
25: 816,984,566,129,618</pre>
25: 816,984,566,129,618</pre>

=={{header|Wren}}==
{{trans|Go}}
{{libheader|Wren-sort}}
{{libheader|Wren-fmt}}
{{libheader|Wren-date}}

A translation of Go's 'traditional' and 'turbo' versions.

Quite a tough task for the little bird which lacks the speed of its compiled, statically typed brethren. Also integer arithmetic is unreliable for numbers >= 2^53 which effectively limits us here to 15 digit rare numbers without resorting to BigInt which would be even slower.

===Traditional===
About 9.5 minutes to find the first 25 rare numbers.
<lang ecmascript>import "/sort" for Sort
import "/fmt" for Fmt

class Term {
construct new(coeff, ix1, ix2) {
_coeff = coeff
_ix1 = ix1
_ix2 = ix2
}
coeff { _coeff }
ix1 { _ix1 }
ix2 { _ix2 }
}

var maxDigits = 15

var toInt = Fn.new { |digits, reverse|
var sum = 0
if (!reverse) {
for (i in 0...digits.count) sum = sum*10 + digits[i]
} else {
for (i in digits.count-1..0) sum = sum*10 + digits[i]
}
return sum
}

var isSquare = Fn.new { |n|
var root = n.sqrt.floor
return root*root == n
}

var seq = Fn.new { |from, to, step|
var res = []
var i = from
while (i <= to) {
res.add(i)
i = i + step
}
return res
}

var start = System.clock
var pow = 1
System.print("Aggregate timings to process all numbers up to:")

// terms of (n-r) expression for number of digits from 2 to maxDigits
var allTerms = List.filled(maxDigits-1, null)
for (r in 2..maxDigits) {
var terms = []
pow = pow * 10
var pow1 = pow
var pow2 = 1
var i1 = 0
var i2 = r - 1
while (i1 < i2) {
terms.add(Term.new(pow1-pow2, i1, i2))
pow1 = (pow1/10).floor
pow2 = pow2 * 10
i1 = i1 + 1
i2 = i2 - 1
}
allTerms[r-2] = terms
}

// map of first minus last digits for 'n' to pairs giving this value
var fml = {
0: [[2, 2], [8, 8]],
1: [[6, 5], [8, 7]],
4: [[4, 0]],
6: [[6, 0], [8, 2]]
}

// map of other digit differences for 'n' to pairs giving this value
var dmd = {}
for (i in 0...100) {
var a = [(i/10).floor, i%10]
var d = a[0] - a[1]
if (dmd[d]) {
dmd[d].add(a)
} else {
dmd[d] = [a]
}
}
var fl = [0, 1, 4, 6]
var dl = seq.call(-9, 9, 1) // all differences
var zl = [0] // zero differences only
var el = seq.call(-8, 8, 2) // even differences only
var ol = seq.call(-9, 9, 2) // odd differences only
var il = seq.call(0, 9, 1)
var rares = []
var lists = List.filled(4, null)
for (i in 0..3) lists[i] = [[fl[i]]]
var digits = []
var count = 0

// Recursive closure to generate (n+r) candidates from (n-r) candidates
// and hence find Rare numbers with a given number of digits.
var fnpr
fnpr = Fn.new { |cand, di, dis, indices, nmr, nd, level|
if (level == dis.count) {
digits[indices[0][0]] = fml[cand[0]][di[0]][0]
digits[indices[0][1]] = fml[cand[0]][di[0]][1]
var le = di.count
if (nd%2 == 1) {
le = le - 1
digits[(nd/2).floor] = di[le]
}
var i = 0
for (d in di[1...le]) {
digits[indices[i+1][0]] = dmd[cand[i+1]][d][0]
digits[indices[i+1][1]] = dmd[cand[i+1]][d][1]
i = i + 1
}
var r = toInt.call(digits, true)
var npr = nmr + 2*r
if (!isSquare.call(npr)) return
count = count + 1
Fmt.write(" R/N $2d:", count)
var ms = ((System.clock - start)*1000).round
Fmt.write(" $,7d ms", ms)
var n = toInt.call(digits, false)
Fmt.print(" ($,d)", n)
rares.add(n)
} else {
for (num in dis[level]) {
di[level] = num
System.write("") // guards against VM recursion bug
fnpr.call(cand, di, dis, indices, nmr, nd, level+1)
}
}
}

// Recursive closure to generate (n-r) candidates with a given number of digits.
var fnmr
fnmr = Fn.new { |cand, list, indices, nd, level|
if (level == list.count) {
var nmr = 0
var nmr2 = 0
var i = 0
for (t in allTerms[nd-2]) {
if (cand[i] >= 0) {
nmr = nmr + t.coeff*cand[i]
} else {
nmr2 = nmr2 - t.coeff*cand[i]
if (nmr >= nmr2) {
nmr = nmr - nmr2
nmr2 = 0
} else {
nmr2 = nmr2 - nmr
nmr = 0
}
}
i = i + 1
}
if (nmr2 >= nmr) return
nmr = nmr - nmr2
if (!isSquare.call(nmr)) return
var dis = []
dis.add(seq.call(0, fml[cand[0]].count-1, 1))
for (i in 1...cand.count) {
dis.add(seq.call(0, dmd[cand[i]].count-1, 1))
}
if (nd%2 == 1) dis.add(il)
var di = List.filled(dis.count, 0)
fnpr.call(cand, di, dis, indices, nmr, nd, 0)
} else {
for (num in list[level]) {
cand[level] = num
System.write("") // guards against VM recursion bug
fnmr.call(cand, list, indices, nd, level+1)
}
}
}

for (nd in 2..maxDigits) {
digits = List.filled(nd, 0)
if (nd == 4) {
lists[0].add(zl)
lists[1].add(ol)
lists[2].add(el)
lists[3].add(ol)
} else if(allTerms[nd-2].count > lists[0].count) {
for (i in 0..3) lists[i].add(dl)
}
var indices = []
for (t in allTerms[nd-2]) indices.add([t.ix1, t.ix2])
for (list in lists) {
var cand = List.filled(list.count, 0)
fnmr.call(cand, list, indices, nd, 0)
}
var ms = ((System.clock - start)*1000).round
Fmt.print(" $2s digits: $,7d ms", nd, ms)
}

Sort.quick(rares)
Fmt.print("\nThe rare numbers with up to $d digits are:\n", maxDigits)
var i = 0
for (rare in rares) {
Fmt.print(" $2d: $,21d", i+1, rare)
i = i + 1
}</lang>

{{out}}
<pre>
Aggregate timings to process all numbers up to:
R/N 1: 0 ms (65)
2 digits: 0 ms
3 digits: 0 ms
4 digits: 0 ms
5 digits: 0 ms
R/N 2: 3 ms (621,770)
6 digits: 3 ms
7 digits: 8 ms
8 digits: 54 ms
R/N 3: 56 ms (281,089,082)
9 digits: 77 ms
R/N 4: 80 ms (2,022,652,202)
R/N 5: 355 ms (2,042,832,002)
10 digits: 1,044 ms
11 digits: 1,742 ms
R/N 6: 4,892 ms (872,546,974,178)
R/N 7: 5,270 ms (872,568,754,178)
R/N 8: 11,318 ms (868,591,084,757)
12 digits: 15,797 ms
R/N 9: 20,673 ms (6,979,302,951,885)
13 digits: 28,328 ms
R/N 10: 79,833 ms (20,313,693,904,202)
R/N 11: 81,291 ms (20,313,839,704,202)
R/N 12: 101,982 ms (20,331,657,922,202)
R/N 13: 104,914 ms (20,331,875,722,202)
R/N 14: 113,507 ms (20,333,875,702,202)
R/N 15: 287,986 ms (40,313,893,704,200)
R/N 16: 290,036 ms (40,351,893,720,200)
14 digits: 340,736 ms
R/N 17: 342,140 ms (200,142,385,731,002)
R/N 18: 345,178 ms (221,462,345,754,122)
R/N 19: 384,744 ms (816,984,566,129,618)
R/N 20: 405,787 ms (245,518,996,076,442)
R/N 21: 409,179 ms (204,238,494,066,002)
R/N 22: 410,135 ms (248,359,494,187,442)
R/N 23: 414,341 ms (244,062,891,224,042)
R/N 24: 511,124 ms (403,058,392,434,500)
R/N 25: 514,313 ms (441,054,594,034,340)
15 digits: 565,126 ms

The rare numbers with up to 15 digits are:

1: 65
2: 621,770
3: 281,089,082
4: 2,022,652,202
5: 2,042,832,002
6: 868,591,084,757
7: 872,546,974,178
8: 872,568,754,178
9: 6,979,302,951,885
10: 20,313,693,904,202
11: 20,313,839,704,202
12: 20,331,657,922,202
13: 20,331,875,722,202
14: 20,333,875,702,202
15: 40,313,893,704,200
16: 40,351,893,720,200
17: 200,142,385,731,002
18: 204,238,494,066,002
19: 221,462,345,754,122
20: 244,062,891,224,042
21: 245,518,996,076,442
22: 248,359,494,187,442
23: 403,058,392,434,500
24: 441,054,594,034,340
25: 816,984,566,129,618
</pre>

===Turbo===
Ruffles the feathers a little with a time 5 times quicker than the 'traditional' version.
<lang ecmascript>import "/sort" for Sort
import "/fmt" for Fmt
import "/date" for Date

class Z2 {
construct new(value, hasValue) {
_value = value
_hasValue = hasValue
}
value { _value }
hasValue { _hasValue }
}

var Pow10 = List.filled(16, 0)

var init = Fn.new {
Pow10[0] = 1
for (i in 1..15) Pow10[i] = 10 * Pow10[i-1]
}

var acc = 0
var bs = List.filled(100000, false)
var L
var H

var izRev
izRev = Fn.new { |n, i, g|
if ((i/Pow10[n-1]).floor != g%10) return false
if (n < 2) return true
System.write("") // guards against VM recursion bug
return izRev.call(n-1, i%Pow10[n-1], (g/10).floor)
}

var fG = Fn.new { |n, start, end, reset, step|
var i = step * start
var g = step * end
var e = step * reset
return Fn.new {
while (i < g) {
acc = acc + step
i = i + step
return Z2.new(acc, true)
}
i = e
acc = acc - (g - e)
return n.call()
}
}

class ZP {
construct new(n, g) {
_n = n
_g = g
}
n { _n }
g { _g }
}

class NLH {
construct new(e) {
var even = []
var odd = []
var n = e.n
var g = e.g
var i = n.call()
while (i.hasValue) {
for (p in g) {
var ng = p[0]
var gg = p[1]
if (ng > 0 || i.value > 0) {
var w = ng*Pow10[4] + gg + i.value
var ws = w.sqrt.floor
if (ws*ws == w) {
if (w%2 == 0) {
even.add(w)
} else {
odd.add(w)
}
}
}
}
i = n.call()
}
_even = even
_odd = odd
}
even { _even }
odd { _odd }
}

var makeL = Fn.new { |n|
var g = List.filled((n/2).floor - 3, null)
g[0] = Fn.new { Z2.new(0, false) }
var i = 1
while (i < (n/2).floor - 3) {
var s = -9
if (i == (n/2).floor - 4) s = -10
var l = Pow10[n-i-4] - Pow10[i+3]
acc = acc + l*s
g[i] = fG.call(g[i-1], s, 9, -9, l)
i = i + 1
}
var g0 = 0
var g1 = 0
var g2 = 0
var g3 = 0
var l0 = Pow10[n-5]
var l1 = Pow10[n-6]
var l2 = Pow10[n-7]
var l3 = Pow10[n-8]
var f = Fn.new {
var w = []
while (g0 < 7) {
var nn = g3*l3 + g2*l2 + g1*l1 + g0*l0
var gg = -1000*g3 - 100*g2 - 10*g1 - g0
if (g3 < 9) {
g3 = g3 + 1
} else {
g3 = -9
if (g2 < 9) {
g2 = g2 + 1
} else {
g2 = -9
if (g1 < 9) {
g1 = g1 + 1
} else {
g1 = -9
if (g0 == 1) g0 = 3
g0 = g0 + 1
}
}
}
if (bs[(Pow10[10]+gg)%10000]) w.add([nn, gg])
}
return w
}
return ZP.new(g[(n/2).floor-4], f.call())
}

var makeH = Fn.new { |n|
acc = -(Pow10[(n/2).floor] + Pow10[((n-1)/2).floor])
var g = List.filled(((n+1)/2).floor - 3, null)
g[0] = Fn.new { Z2.new(0, false) }
var i = 1
while (i < (n/2).floor - 3) {
var j = 0
if (i == ((n+1)/2).floor - 3) j = -1
g[i] = fG.call(g[i-1], j, 18, 0, Pow10[n-i-4]+Pow10[i+3])
if (n%2 == 1) {
g[((n+1)/2).floor-4] = fG.call(g[(n/2).floor-4], -1, 9, 0, 2*Pow10[(n/2).floor])
}
i = i + 1
}
var g0 = 4
var g1 = 0
var g2 = 0
var g3 = 0
var l0 = Pow10[n-5]
var l1 = Pow10[n-6]
var l2 = Pow10[n-7]
var l3 = Pow10[n-8]
var f = Fn.new {
var w = []
while (g0 < 17) {
var nn = g3*l3 + g2*l2 + g1*l1 + g0*l0
var gg = 1000*g3 + 100*g2 + 10*g1 + g0
if (g3 < 18) {
g3 = g3 + 1
} else {
g3 = 0
if (g2 < 18) {
g2 = g2 + 1
} else {
g2 = 0
if (g1 < 18) {
g1 = g1 + 1
} else {
g1 = 0
if (g0 == 6 || g0 == 9) g0 = g0 + 3
g0 = g0 + 1
}
}
}
if (bs[gg%10000]) w.add([nn, gg])
}
return w
}
return ZP.new(g[((n+1)/2).floor-4], f.call())
}

var rare = Fn.new { |n|
acc = 0
for (g in 0...10000) bs[(g*g)%10000] = true
L = NLH.new(makeL.call(n))
H = NLH.new(makeH.call(n))
var rares = []
for (l in L.even) {
for (h in H.even) {
var r = ((h - l)/2).floor
var z = h - r
if (izRev.call(n, r, z)) rares.add(z)
}
}
for (l in L.odd) {
for (h in H.odd) {
var r = ((h - l)/2).floor
var z = h - r
if (izRev.call(n, r, z)) rares.add(z)
}
}
if (rares.count > 0) Sort.quick(rares)
return rares
}

// Formats time in form hh:mm:ss.fff (i.e. millisecond precision).
var formatTime = Fn.new { |d|
var ms = (d * 1000).round
var tm = Date.fromNumber(ms)
Date.default = Date.isoTime + "|.|ttt"
return tm.toString
}

var bStart = System.clock // block time
var tStart = bStart // total time
init.call()
var nth = 3 // i.e. count of rare numbers < 10 digits
System.print("nth rare number digs block time total time")
for (nd in 10..15) {
var rares = rare.call(nd)
if (rares.count > 0) {
var i = 0
for (r in rares) {
nth = nth + 1
var t = ""
if (i < rares.count - 1) t = "\n"
Fmt.write("$2d $,21d$s", nth, r, t)
i = i + 1
}
} else {
Fmt.write("$26s", "")
}
var fbTime = formatTime.call(System.clock - bStart)
var ftTime = formatTime.call(System.clock - tStart)
Fmt.print(" $2d: $s $s", nd, fbTime, ftTime)
bStart = System.clock // restart block timing
}</lang>

{{out}}
<pre>
nth rare number digs block time total time
4 2,022,652,202
5 2,042,832,002 10: 00:00:00.084 00:00:00.084
11: 00:00:00.262 00:00:00.346
6 868,591,084,757
7 872,546,974,178
8 872,568,754,178 12: 00:00:00.714 00:00:01.060
9 6,979,302,951,885 13: 00:00:04.601 00:00:05.662
10 20,313,693,904,202
11 20,313,839,704,202
12 20,331,657,922,202
13 20,331,875,722,202
14 20,333,875,702,202
15 40,313,893,704,200
16 40,351,893,720,200 14: 00:00:13.764 00:00:19.426
17 200,142,385,731,002
18 204,238,494,066,002
19 221,462,345,754,122
20 244,062,891,224,042
21 245,518,996,076,442
22 248,359,494,187,442
23 403,058,392,434,500
24 441,054,594,034,340
25 816,984,566,129,618 15: 00:01:34.206 00:01:53.632
</pre>

===Quicker===
===Quicker===
{{trans|C#}} (translation of the quicker version)
{{trans|C#}} (translation of the quicker version)