Rare numbers: Difference between revisions

Content deleted Content added
PureFox (talk | contribs)
→‎Version 1: Replaced with much faster version.
PureFox (talk | contribs)
→‎Version 2: Replaced with version which has relatively low memory requirement.
Line 363: Line 363:


===Version 2===
===Version 2===
To deal with the high memory overhead of the first version, this version delivers the Cartesian products 100 at a time rather than ''en masse''. Not surprisingly, this version is slower than the first and its now taking about 43 seconds to get to 15 digits. However, the pay-off is that 16 and 17 digit numbers can now be processed in a reasonable time without running out of memory.
This is based on Nigel Galloway's approach (see Talk page) of working from (n-r) and deducing the Rare numbers with various numbers of digits from there.

It is more than 500 times faster than Version 1, taking 1.3, 2.3, 28.5 and 42.1 seconds to process all 12, 13, 14 and 15 digit numbers respectively. However, it uses a lot of memory for the Cartesian products.
<lang go>package main
<lang go>package main


Line 380: Line 378:
}
}


const maxDigits = 15
const (
maxDigits = 17
chanBufSize = 100
)


func toInt64(digits []int8, reverse bool) int64 {
func toInt64(digits []int8, reverse bool) int64 {
Line 397: Line 398:


func isSquare(n int64) bool {
func isSquare(n int64) bool {
if 0x202021202030213&(1<<(n&63)) != 0 {
root := int64(math.Sqrt(float64(n)))
return root*root == n
root := int64(math.Sqrt(float64(n)))
return root*root == n
}
return false
}
}


// From the Cartesian product of two or more lists task - Extra credit 1.
// From the Cartesian product of two or more lists task - Extra credit 1.
// Changed to yield results 'chanBufSize' at a time rather than en masse.
func cp(a ...[]int8) [][]int8 {
func cp(ch chan<- []int8, a ...[]int8) {
c := 1
c := 1
for _, a := range a {
for _, a := range a {
Line 408: Line 413:
}
}
if c == 0 {
if c == 0 {
return nil
close(ch)
return
}
}
p := make([][]int8, c)
b := make([]int8, c*len(a))
b := make([]int8, c*len(a))
n := make([]int8, len(a))
n := make([]int8, len(a))
s := 0
s := 0
for i := range p {
for i := 0; i < c; i++ {
e := s + len(a)
e := s + len(a)
pi := b[s:e]
pi := b[s:e]
p[i] = pi
s = e
for j, n := range n {
for j, n := range n {
pi[j] = a[j][n]
pi[j] = a[j][n]
Line 429: Line 432:
n[j] = 0
n[j] = 0
}
}
ch <- pi
s = e
}
}
return p
close(ch)
}
}


func seq(from, to int8) []int8 {
func seq(from, to, step int8) []int8 {
var res []int8
var res []int8
for i := from; i <= to; i++ {
for i := from; i <= to; i += step {
res = append(res, i)
res = append(res, i)
}
}
Line 455: Line 460:
// terms of (n-r) expression for number of digits from 2 to maxDigits
// terms of (n-r) expression for number of digits from 2 to maxDigits
allTerms := make([][]term, maxDigits-1)
allTerms := make([][]term, maxDigits-1)
fmt.Println("Aggregate timings to process all numbers up to:")
for r := 2; r <= maxDigits; r++ {
for r := 2; r <= maxDigits; r++ {
var terms []term
var terms []term
Line 466: Line 472:
allTerms[r-2] = terms
allTerms[r-2] = terms
}
}
// map of first minus last digits for 'n' to pairs giving this value
// map of first minus last digits for 'n' to pairs giving this value
fml := map[int8][][]int8{
fml := map[int8][][]int8{
0: {{2, 2}, {8, 8}},
0: {{2, 2}, {8, 8}},
1: {{6, 5}, {8, 7}},
1: {{6, 5}, {8, 7}},
4: {{4, 0}},
4: {{4, 0}},
5: {{8, 3}},
6: {{6, 0}, {8, 2}},
6: {{6, 0}, {8, 2}},
}
}
Line 481: Line 486:
dmd[d] = append(dmd[d], a)
dmd[d] = append(dmd[d], a)
}
}
fl := []int8{0, 1, 4, 5, 6}
fl := []int8{0, 1, 4, 6}
dl := seq(-9, 9)
dl := seq(-9, 9, 1) // all differences
il := seq(0, 9)
zl := []int8{0} // zero differences only
el := seq(-8, 8, 2) // even differences only
ol := seq(-9, 9, 2) // odd differences only
il := seq(0, 9, 1)
var rares []int64
var rares []int64
lists := [][]int8{fl}
lists := make([][][]int8, 4)
for i, f := range fl {
fmt.Printf("The rare numbers with up to %d digits are:\n", maxDigits)
lists[i] = [][]int8{{f}}
}
for nd := 2; nd <= maxDigits; nd++ {
for nd := 2; nd <= maxDigits; nd++ {
digits := make([]int8, nd)
digits := make([]int8, nd)
if len(allTerms[nd-2]) > len(lists) {
if nd == 4 {
lists = append(lists, dl)
lists[0] = append(lists[0], zl)
lists[1] = append(lists[1], ol)
lists[2] = append(lists[2], el)
lists[3] = append(lists[3], ol)
} else if len(allTerms[nd-2]) > len(lists[0]) {
for i := 0; i < 4; i++ {
lists[i] = append(lists[i], dl)
}
}
}
var indices [][2]int8
var indices [][2]int8
Line 496: Line 513:
indices = append(indices, [2]int8{t.ix1, t.ix2})
indices = append(indices, [2]int8{t.ix1, t.ix2})
}
}

cands := cp(lists...)
for _, cand := range cands {
for _, list := range lists {
nmr := int64(0)
ch := make(chan []int8, chanBufSize)
for i, t := range allTerms[nd-2] {
go cp(ch, list...)
nmr += t.coeff * int64(cand[i])
for cand := range ch {
}
nmr := int64(0)
if nmr <= 0 || !isSquare(nmr) {
for i, t := range allTerms[nd-2] {
continue
nmr += t.coeff * int64(cand[i])
}
var dis [][]int8
dis = append(dis, seq(0, int8(len(fml[cand[0]]))-1))
for i := 1; i < len(cand); i++ {
dis = append(dis, seq(0, int8(len(dmd[cand[i]]))-1))
}
if nd%2 == 1 {
dis = append(dis, il)
}
dis = cp(dis...)
for _, di := range dis {
digits[indices[0][0]] = fml[cand[0]][di[0]][0]
digits[indices[0][1]] = fml[cand[0]][di[0]][1]
le := len(di)
if nd%2 == 1 {
le--
digits[nd/2] = di[le]
}
for i, d := range 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]
}
}
r := toInt64(digits, true)
if nmr <= 0 || !isSquare(nmr) {
npr := nmr + 2*r
if !isSquare(npr) {
continue
continue
}
}
rares = append(rares, toInt64(digits, false))
var dis [][]int8
dis = append(dis, seq(0, int8(len(fml[cand[0]]))-1, 1))
for i := 1; i < len(cand); i++ {
dis = append(dis, seq(0, int8(len(dmd[cand[i]]))-1, 1))
}
if nd%2 == 1 {
dis = append(dis, il)
}
ch2 := make(chan []int8, chanBufSize)
go cp(ch2, dis...)
for di := range ch2 {
digits[indices[0][0]] = fml[cand[0]][di[0]][0]
digits[indices[0][1]] = fml[cand[0]][di[0]][1]
le := len(di)
if nd%2 == 1 {
le--
digits[nd/2] = di[le]
}
for i, d := range 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]
}
r := toInt64(digits, true)
npr := nmr + 2*r
if !isSquare(npr) {
continue
}
rares = append(rares, toInt64(digits, false))
}
}
}
}
}
fmt.Printf(" %2d digits: %9s ms\n", nd, commatize(time.Since(start).Milliseconds()))
}
}
sort.Slice(rares, func(i, j int) bool { return rares[i] < rares[j] })
sort.Slice(rares, func(i, j int) bool { return rares[i] < rares[j] })
fmt.Printf("\nThe rare numbers with up to %d digits are:\n", maxDigits)
for i, rare := range rares {
for i, rare := range rares {
fmt.Printf(" %2d: %19s\n", i+1, commatize(rare))
fmt.Printf(" %2d: %22s\n", i+1, commatize(rare))
}
}
fmt.Printf("\nUp to %d digits processed in %s\n", maxDigits, time.Since(start))
}</lang>
}</lang>


{{output}}
{{output}}
<pre>
<pre>
Aggregate timings to process all numbers up to:
The rare numbers with up to 15 digits are:
1: 65
2 digits: 0 ms
2: 621,770
3 digits: 0 ms
3: 281,089,082
4 digits: 0 ms
4: 2,022,652,202
5 digits: 0 ms
5: 2,042,832,002
6 digits: 0 ms
6: 868,591,084,757
7 digits: 1 ms
7: 872,546,974,178
8 digits: 6 ms
8: 872,568,754,178
9 digits: 9 ms
9: 6,979,302,951,885
10 digits: 118 ms
11 digits: 180 ms
10: 20,313,693,904,202
12 digits: 1,363 ms
11: 20,313,839,704,202
13 digits: 2,323 ms
12: 20,331,657,922,202
14 digits: 26,586 ms
13: 20,331,875,722,202
15 digits: 43,034 ms
14: 20,333,875,702,202
16 digits: 405,029 ms
15: 40,313,893,704,200
17 digits: 678,839 ms
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


The rare numbers with up to 17 digits are:
Up to 15 digits processed in 42.075636626s
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
26: 2,078,311,262,161,202
27: 2,133,786,945,766,212
28: 2,135,568,943,984,212
29: 2,135,764,587,964,212
30: 2,135,786,765,764,212
31: 4,135,786,945,764,210
32: 6,157,577,986,646,405
33: 6,889,765,708,183,410
34: 8,052,956,026,592,517
35: 8,052,956,206,592,517
36: 8,191,154,686,620,818
37: 8,191,156,864,620,818
38: 8,191,376,864,400,818
39: 8,650,327,689,541,457
40: 8,650,349,867,341,457
41: 22,542,040,692,914,522
42: 67,725,910,561,765,640
43: 86,965,750,494,756,968
</pre>
</pre>