2016 Update

This commit is contained in:
Tina Müller 2016-12-05 22:15:40 +01:00
parent 948b86eafa
commit dcf5d15da3
7965 changed files with 139854 additions and 31002 deletions

View file

@ -1,12 +1,20 @@
'''[[wp:Hamming numbers|Hamming numbers]]''' are numbers of the form
: <math>H = 2^i \cdot 3^j \cdot 5^k, \; \mathrm{where} \; i, j, k \geq 0</math>.
''Hamming numbers'' are also known as ''ugly numbers'' and also ''5-smooth numbers'' &nbsp; (numbers whose prime divisors are less or equal to 5).
'''[[wp:Hamming numbers|Hamming numbers]]''' are numbers of the form &nbsp;
<big><big> H = 2<sup>i</sup> &times; 3<sup>j</sup> &times; 5<sup>k</sup> </big></big>
where
<big> i, j, k ≥ 0 </big>
Generate the sequence of Hamming numbers, ''in increasing order''. In particular:
# Show the first twenty Hamming numbers.
# Show the 1691st Hamming number (the last one below <math>2^{31}</math>).
# Show the one millionth Hamming number (if the language or a convenient library supports arbitrary-precision integers).
'''References'''
# [[wp:Hamming numbers|Hamming numbers]]
# [[wp:Smooth number|Smooth number]]
# [http://dobbscodetalk.com/index.php?option=com_content&task=view&id=913&Itemid=85 Hamming problem] from Dr. Dobb's CodeTalk (dead link as of Sep 2011; parts of the thread [http://drdobbs.com/blogs/architecture-and-design/228700538 here] and [http://www.jsoftware.com/jwiki/Essays/Hamming%20Number here]).
''Hamming numbers'' &nbsp; are also known as &nbsp; ''ugly numbers'' &nbsp; and also &nbsp; ''5-smooth numbers'' &nbsp; (numbers whose prime divisors are less or equal to 5).
;Task:
Generate the sequence of Hamming numbers, ''in increasing order''. &nbsp; In particular:
# Show the &nbsp; first twenty &nbsp; Hamming numbers.
# Show the &nbsp; 1691<sup>st</sup> &nbsp; Hamming number (the last one below &nbsp; 2<sup>31</sup>).
# Show the &nbsp; one million<sup>th</sup> &nbsp; Hamming number (if the language or a convenient library supports arbitrary-precision integers).
;References:
* [[wp:Hamming numbers|Hamming numbers]]
* [[wp:Smooth number|Smooth number]]
* [http://dobbscodetalk.com/index.php?option=com_content&task=view&id=913&Itemid=85 Hamming problem] from Dr. Dobb's CodeTalk (dead link as of Sep 2011; parts of the thread [http://drdobbs.com/blogs/architecture-and-design/228700538 here] and [http://www.jsoftware.com/jwiki/Essays/Hamming%20Number here]).
<br><br>

View file

@ -2,12 +2,12 @@
"Computes the unbounded sequence of Hamming 235 numbers."
[]
(letfn [(merge [xs ys]
(let [xv (first xs), yv (first ys)]
(if (< xv yv) (cons xv (lazy-seq (merge (next xs) ys)))
(cons yv (lazy-seq (merge xs (next ys))))))),
(if (nil? xs) ys
(let [xv (first xs), yv (first ys)]
(if (< xv yv) (cons xv (lazy-seq (merge (next xs) ys)))
(cons yv (lazy-seq (merge xs (next ys)))))))),
(smult [m s] ;; equiv to map (* m) s -- faster
(cons (*' m (first s)) (lazy-seq (smult m (next s)))))]
(do (def s5 (cons 5 (lazy-seq (smult 5 s5))))
(def s35 (cons 3 (lazy-seq (merge s5 (smult 3 s35)))))
(def s235 (cons 2 (lazy-seq (merge s35 (smult 2 s235)))))
(cons 1 (lazy-seq s235)))))
(cons (*' m (first s)) (lazy-seq (smult m (next s))))),
(u [s n] (let [r (atom nil)]
(reset! r (merge s (smult n (cons 1 (lazy-seq @r)))))))]
(cons 1 (lazy-seq (reduce u nil (list 5 3 2))))))

View file

@ -0,0 +1,110 @@
// Hamming project main.go
package main
import (
"fmt"
"math/big"
"time"
)
type lazyList struct {
head *big.Int
tail *lazyList
contf func() *lazyList
}
func (oll *lazyList) next() *lazyList {
if oll.contf != nil { // not thread-safe
oll.tail = oll.contf()
oll.contf = nil
}
return oll.tail
}
func merge(a *lazyList, b *lazyList) *lazyList {
rslt := new(lazyList)
x := a.head
y := b.head
if x.Cmp(y) < 0 {
rslt.head = x
rslt.contf = func() *lazyList {
return merge(a.next(), b)
}
} else {
rslt.head = y
rslt.contf = func() *lazyList {
return merge(a, b.next())
}
}
return rslt
}
func llmult(m *big.Int, ll *lazyList) *lazyList {
rslt := new(lazyList)
rslt.head = new(big.Int).Set(big.NewInt(0)).Mul(m, ll.head)
rslt.contf = func() *lazyList {
return llmult(m, ll.next())
}
return rslt
}
func u(s *lazyList, n *big.Int) *lazyList {
rslt := new(lazyList)
cr := new(lazyList)
cr.head = big.NewInt(1)
cr.contf = func() *lazyList {
return rslt
}
if s == nil {
rslt = llmult(n, cr)
} else {
rslt = merge(s, llmult(n, cr))
}
return rslt
}
func Hamming() func() *big.Int {
prms := []int64{5, 3, 2}
curr := new(lazyList)
curr.head = big.NewInt(1)
curr.contf = func() *lazyList {
var r *lazyList = nil
for _, v := range prms {
r = u(r, big.NewInt(v))
}
return r
}
return func() *big.Int {
temp := curr
curr = curr.next()
return temp.head
}
}
func main() {
n := 1000000
hamiter := Hamming()
rarr := make([]*big.Int, 20)
for i, _ := range rarr {
rarr[i] = hamiter()
}
fmt.Println(rarr)
hamiter = Hamming()
for i := 1; i < 1691; i++ {
hamiter()
}
fmt.Println(hamiter())
strt := time.Now()
hamiter = Hamming()
for i := 1; i < n; i++ {
hamiter()
}
rslt := hamiter()
end := time.Now()
fmt.Printf("Found the %vth Hamming number as %v in %v.\r\n", n, rslt.String(), end.Sub(strt))
}

View file

@ -0,0 +1,148 @@
package main
import (
"fmt"
"math/big"
"time"
)
// constants as expanded integers to minimize round-off errors, and
// reduce execution time using integer operations not float...
const cLAA2 uint64 = 35184372088832 // 2.0f64.ln() * 2.0f64.powi(45)).round() as u64;
const cLBA2 uint64 = 55765910372219 // 3.0f64.ln() / 2.0f64.ln() * 2.0f64.powi(45)).round() as u64;
const cLCA2 uint64 = 81695582054030 // 5.0f64.ln() / 2.0f64.ln() * 2.0f64.powi(45)).round() as u64;
type logelm struct { // log representation of an element with only allowable powers
exp2 uint16
exp3 uint16
exp5 uint16
logr uint64 // log representation used for comparison only - not exact
}
func (self *logelm) lte(othr *logelm) bool {
if self.logr <= othr.logr {
return true
} else {
return false
}
}
func (self *logelm) mul2() logelm {
return logelm{
exp2: self.exp2 + 1,
exp3: self.exp3,
exp5: self.exp5,
logr: self.logr + cLAA2,
}
}
func (self *logelm) mul3() logelm {
return logelm{
exp2: self.exp2,
exp3: self.exp3 + 1,
exp5: self.exp5,
logr: self.logr + cLBA2,
}
}
func (self *logelm) mul5() logelm {
return logelm{
exp2: self.exp2,
exp3: self.exp3,
exp5: self.exp5 + 1,
logr: self.logr + cLCA2,
}
}
func log_nodups_hamming(n uint) *big.Int {
if n < 1 {
panic("log_nodups_hamming: argument < 1!")
}
if n < 2 { // trivial case of first in sequence
return big.NewInt(1)
}
if n > 1.2e15 {
panic("log_nodups_hamming: argument too large!")
}
one := logelm{}
next5, merge := one.mul5(), one.mul3()
next53, next532 := merge.mul3(), one.mul2()
g := make([]logelm, 1, 65536)
g[0] = one // never used, just so append works
h := make([]logelm, 1, 65536)
h[0] = one // never used, just so append works
i, j := 1, 1
for m := uint(1); m < n; m++ {
cph := cap(h)
if i >= cph/2 {
nm := copy(h[0:i], h[i:])
h = h[0:nm:cph]
i = 0
}
if next532.lte(&merge) {
h = append(h, next532)
next532 = h[i].mul2()
i++
} else {
h = append(h, merge)
if next53.lte(&next5) {
merge = next53
next53 = g[j].mul3()
j++
} else {
merge = next5
next5 = next5.mul5()
}
cpg := cap(g)
if j >= cpg/2 {
nm := copy(g[0:j], g[j:])
g = g[0:nm:cpg]
j = 0
}
g = append(g, merge)
}
}
two, three, five := big.NewInt(2), big.NewInt(3), big.NewInt(5)
o := h[len(h)-1] // convert last element to big integer...
ob := big.NewInt(1)
for i := uint16(0); i < o.exp2; i++ {
ob.Mul(two, ob)
}
for i := uint16(0); i < o.exp3; i++ {
ob.Mul(three, ob)
}
for i := uint16(0); i < o.exp5; i++ {
ob.Mul(five, ob)
}
return ob
}
func main() {
n := uint(1e6)
rarr := make([]*big.Int, 20)
for i, _ := range rarr {
rarr[i] = log_nodumps_hamming(i)
}
fmt.Println(rarr)
fmt.Println(log_nodups_hamming(1691))
strt := time.Now()
rslt := log_nodups_hamming(n)
end := time.Now()
rs := rslt.String()
lrs := len(rs)
fmt.Printf("%v digits:\r\n", lrs)
ndx := 0
for ; ndx < lrs-100; ndx += 100 {
fmt.Println(rs[ndx : ndx+100])
}
fmt.Println(rs[ndx:])
fmt.Printf("This last found the %vth hamming number in %v.\r\n", n, end.Sub(strt))
}

View file

@ -0,0 +1,122 @@
package main
import (
"fmt"
"math"
"math/big"
"sort"
"time"
)
type logrep struct {
lg float64
x2, x3, x5 uint32
}
type logreps []logrep
func (s logreps) Len() int { // necessary methods for sorting
return len(s)
}
func (s logreps) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s logreps) Less(i, j int) bool {
return s[j].lg < s[i].lg // sort in decreasing order (reverse order compare)
}
func nthHamming(n uint64) (uint32, uint32, uint32) {
if n < 2 {
if n < 1 {
panic("nthHamming: argument is zero!")
}
return 0, 0, 0
}
const lb3 = 1.5849625007211561814537389439478 // math.Log2(3.0)
const lb5 = 2.3219280948873623478703194294894 // math.Log2(5.0)
fctr := 6.0 * lb3 * lb5
crctn := math.Log2(math.Sqrt(30.0)) // from WP formula
lgest := math.Pow(fctr*float64(n), 1.0/3.0) - crctn
var frctn float64
if n < 1000000000 {
frctn = 0.509
} else {
frctn = 0.106
}
lghi := math.Pow(fctr*(float64(n)+frctn*lgest), 1.0/3.0) - crctn
lglo := 2.0*lgest - lghi // and a lower limit of the upper "band"
var count uint64 = 0
bnd := make(logreps, 0) // give it one value so doubling size works
klmt := uint32(lghi/lb5) + 1
for k := uint32(0); k < klmt; k++ {
p := float64(k) * lb5
jlmt := uint32((lghi-p)/lb3) + 1
for j := uint32(0); j < jlmt; j++ {
q := p + float64(j)*lb3
ir := lghi - q
lg := q + math.Floor(ir) // current log value estimated
count += uint64(ir) + 1
if lg >= lglo {
bnd = append(bnd, logrep{lg, uint32(ir), j, k})
}
}
}
if n > count {
panic("nthHamming: band high estimate is too low!")
}
ndx := int(count - n)
if ndx >= bnd.Len() {
panic("nthHamming: band low estimate is too high!")
}
sort.Sort(bnd) // sort decreasing order due definition of Less above
rslt := bnd[ndx]
return rslt.x2, rslt.x3, rslt.x5
}
func convertTpl2BigInt(x2, x3, x5 uint32) *big.Int {
result := big.NewInt(1)
two := big.NewInt(2)
three := big.NewInt(3)
five := big.NewInt(5)
for i := uint32(0); i < x2; i++ {
result.Mul(result, two)
}
for i := uint32(0); i < x3; i++ {
result.Mul(result, three)
}
for i := uint32(0); i < x5; i++ {
result.Mul(result, five)
}
return result
}
func main() {
for i := 1; i <= 20; i++ {
fmt.Printf("%v ", convertTpl2BigInt(nthHamming(uint64(i))))
}
fmt.Println()
fmt.Println(convertTpl2BigInt(nthHamming(1691)))
strt := time.Now()
x2, x3, x5 := nthHamming(uint64(1e6))
end := time.Now()
fmt.Printf("2^%v times 3^%v times 5^%v\r\n", x2, x3, x5)
lrslt := convertTpl2BigInt(x2, x3, x5)
lgrslt := (float64(x2) + math.Log2(3.0)*float64(x3) +
math.Log2(5.0)*float64(x5)) * math.Log10(2.0)
exp := math.Floor(lgrslt)
mant := math.Pow(10.0, lgrslt-exp)
fmt.Printf("Approximately: %vE+%v\r\n", mant, exp)
rs := lrslt.String()
lrs := len(rs)
fmt.Printf("%v digits:\r\n", lrs)
if lrs <= 10000 {
ndx := 0
for ; ndx < lrs-100; ndx += 100 {
fmt.Println(rs[ndx : ndx+100])
}
fmt.Println(rs[ndx:])
}
fmt.Printf("This last found the %vth hamming number in %v.\r\n", n, end.Sub(strt))
}

View file

@ -1,12 +1,13 @@
hamming = 1:foldl u [] [5,3,2] where
u s n = ar where
ar = merge s (n:map (n*) ar)
merge [] b = b
merge a@(x:xs) b@(y:ys)
| x < y = x:merge xs b
| otherwise = y:merge a ys
hamming = 1 : foldr u [] [2,3,5] where
u n s = -- fix (merge s . map (n*) . (1:))
r where
r = merge s (map (n*) (1:r))
merge [] b = b
merge a@(x:xs) b@(y:ys) | x < y = x : merge xs b
| otherwise = y : merge a ys
main = do
print $ take 20 hamming
print $ hamming !! 1690
print $ hamming !! (1000000-1)
print $ take 20 (hamming ())
print $ (hamming ()) !! 1690
print $ (hamming ()) !! (1000000-1)

View file

@ -1,44 +1,40 @@
-- directly find n-th Hamming number, in ~ O(n^{2/3}) time
-- by Will Ness, based on "top band" idea by Louis Klauder, from DDJ discussion
-- http://drdobbs.com/blogs/architecture-and-design/228700538
-- based on "top band" idea by Louis Klauder, from DDJ discussion
-- by Will Ness, original post: drdobbs.com/blogs/architecture-and-design/228700538
{-# OPTIONS -O2 -XBangPatterns #-}
import Data.List (sortBy)
import Data.Function (on)
import Data.List
import Data.Function
main = let (r,t) = nthHam 1000000 in print t >> print (trival t)
lg3 = logBase 2 3; lg5 = logBase 2 5
logval (i,j,k) = fromIntegral i + fromIntegral j*lg3 + fromIntegral k*lg5
lb3 = logBase 2 3; lb5 = logBase 2 5; lb30_2 = logBase 2 30 / 2
trival (i,j,k) = 2^i * 3^j * 5^k
estval n = (6*lg3*lg5* fromIntegral n)**(1/3) -- estimated logval, base 2
rngval n
| n > 500000 = (2.4496 , 0.0076 ) -- empirical estimation
| n > 50000 = (2.4424 , 0.0146 ) -- correction, base 2
| n > 500 = (2.3948 , 0.0723 ) -- (dist,width)
| n > 1 = (2.2506 , 0.2887 ) -- around (log $ sqrt 30),
| otherwise = (2.2506 , 0.5771 ) -- says WP
estval n
| n > 500000 = (v - lb30_2 + (3/v), 6/v) -- the space tweak! (thx, GBG!)
| n > 500000 = (v - 2.4496 , 0.0076 ) -- empirical estimation
| n > 50000 = (v - 2.4424 , 0.0146 ) -- correction, base 2
| n > 500 = (v - 2.3948 , 0.0723 ) -- (dist,width)
| n > 1 = (v - 2.2506 , 0.2887 ) -- around (log $ sqrt 30),
| otherwise = (v - 2.2506 , 0.5771 ) -- says WP
where v = (6*lb3*lb5* fromIntegral n)**(1/3) -- estimated logval, base 2
nthHam :: Int -> (Double, (Int, Int, Int))
nthHam n -- n: 1-based: 1,2,3...
nthHam :: Integer -> (Double, (Int, Int, Int)) -- ( 64bit: use Int!!! NB! )
nthHam n -- n: 1-based: 1,2,3...
| n <= 0 = error $ "n is 1--based: must be n > 0: " ++ show n
| w >= 1 = error $ "Breach of contract: (w < 1): " ++ show w
| m < 0 = error $ "Not enough triples generated: " ++ show (c,n)
| m >= nb = error $ "Generated band is too narrow: " ++ show (m,nb)
| otherwise = res
| otherwise = sortBy (flip compare `on` fst) b !! m -- m-th from top in sorted band
where
(d,w) = rngval n -- correction dist, width
hi = estval n - d -- hi > logval > hi-w
(m,nb) = ( fromIntegral $ c - n, length b ) -- m 0-based from top, |band|
(s,res) = ( sortBy (flip compare `on` fst) b, s!!m ) -- sorted decreasing, result
(c,b) = f 0 -- total count, the band
[ ( i+1, -- total triples w/ this (j,k)
[ (r,(i,j,k)) | frac < w ] ) -- store it, if inside band
| k <- [ 0 .. floor ( hi /lg5) ], let p = fromIntegral k*lg5,
j <- [ 0 .. floor ((hi-p)/lg3) ], let q = fromIntegral j*lg3 + p,
let (i,frac) = pr (hi-q) ; r = hi-frac ] -- r = i + q
-- f 0 z == (sum $ map fst z, concat $ map snd z)
where pr = properFraction
f !c [] = (c,[]) -- code as a loop
f !c ((c1,b1):r) = let (cr,br) = f (c+c1) r -- to prevent space leak
in case b1 of { [v] -> (cr,v:br)
; _ -> (cr, br) }
(hi,w) = estval n -- hi > logval > hi-w
m = fromIntegral (c - n) -- target index, from top
nb = length b -- length of the band
(c,b) = foldl_ (\(c,b) (i,t)-> let c2=c+i in c2`seq` -- ( total count, the band )
case t of []-> (c2,b);[v]->(c2,v:b) ) (0,[]) -- ( =~= mconcat )
[ ( fromIntegral i+1, -- total triples w/ this (j,k)
[ (r,(i,j,k)) | frac < w ] ) -- store it, if inside band
| k <- [ 0 .. floor ( hi /lb5) ], let p = fromIntegral k*lb5,
j <- [ 0 .. floor ((hi-p)/lb3) ], let q = fromIntegral j*lb3 + p,
let (i,frac) = pr (hi-q) ; r = hi - frac -- r = i + q
] where pr = properFraction -- pr 1.24 => (1,0.24)
foldl_ = foldl'

View file

@ -0,0 +1,43 @@
{-# OPTIONS -O2 -XBangPatterns #-}
import Data.Word
import Data.List (sortBy)
import Data.Function (on)
main = let t = nthHam 1000000000000 in print t >> print (trival t)
lb3 = logBase 2 3; lb5 = logBase 2 5
lbrt30 = logBase 2 $ sqrt 30 :: Double -- estimate adjustment as per WP
trival (i,j,k) = 2^i * 3^j * 5^k
estval2 n = (6*lb3*lb5*n)**(1/3) - lbrt30 -- estimated logval, base 2
crctn n
| n < 1000 = 0.509 -- empirical correction terms
| n < 1000000 = 0.206
| n < 1000000000 = 0.122 -- further divisions have little effect as already small
| otherwise = 0.105 -- very slowly decrease from this point for a billion
nthHam :: Word64 -> (Int, Int, Int)
nthHam n -- n: 1-based 1,2,3...
| n < 2 = case n of
0 -> error "nthHam: Argument is zero!"
_ -> (0, 0, 0) -- trivial case for 1
| m < 0 = error $ "Not enough triples generated: " ++ show (c,n)
| m >= nb = error $ "Generated band is too narrow: " ++ show (m,nb)
| otherwise = case res of (_, tv) -> tv -- 2^i * 3^j * 5^k
where
(fr,est)= (crctn n, estval2 $ fromIntegral n) -- fraction of log2 error, est val
(hi,lo) = (estval2 (fromIntegral n + fr*est), 2*est-hi) -- hi > logval2 > hi-w
(c,b) = let klmt = floor (hi/lb5) in
let loopk k !ck bndk =
if k > klmt then (ck, bndk) else
let p = fromIntegral k*lb5; jlmt = floor ((hi-p)/lb3) in
let loopj j !cj bndj =
if j > jlmt then loopk (k+1) cj bndj else
let q = fromIntegral j*lb3 + p in
let (i, frac) = properFraction (hi-q); r = hi-frac in
if r < lo then loopj (j+1) (fromIntegral i+cj+1) bndj else
loopj (j+1) (fromIntegral i+cj+1) ((r,(i,j,k)):bndj) in
loopj 0 ck bndk in
loopk 0 0 []
(m,nb) = ( fromIntegral $ c - n, length b ) -- m 0-based from top, |band|
(s,res) = ( sortBy (flip compare `on` fst) b, s!!m ) -- sorted decreasing, result<

View file

@ -5,15 +5,15 @@ val One = BigInteger.ONE
val Three = BigInteger.valueOf(3)
val Five = BigInteger.valueOf(5)
fun PriorityQueue<BigInteger>.update(x: BigInteger) {
add(x shiftLeft(1))
add(x multiply(Three))
add(x multiply(Five))
fun PriorityQueue<BigInteger>.update(x: BigInteger) : PriorityQueue<BigInteger> {
add(x.shiftLeft(1))
add(x.multiply(Three))
add(x.multiply(Five))
return this
}
fun hamming(n: Int): BigInteger {
val frontier = PriorityQueue<BigInteger>()
frontier.update(One)
val frontier = PriorityQueue<BigInteger>().update(One)
var lowest = One
repeat(n - 1) {
lowest = frontier.poll() ?: lowest
@ -29,5 +29,5 @@ fun hamming(i : Iterable<Int>) : Iterable<BigInteger> = i.map { hamming(it) }
fun main(args: Array<String>) {
val r = 1..20
println("Hamming($r) = " + hamming(r))
arrayOf(1691, 1000000).forEach { println("Hamming(${it}) = " + hamming(it)) }
arrayOf(1691, 1000000).forEach { println("Hamming($it) = " + hamming(it)) }
}

View file

@ -5,16 +5,16 @@ val One = BigInteger.ONE
val Three = BigInteger.valueOf(3)
val Five = BigInteger.valueOf(5)
fun PriorityQueue<BigInteger>.update(x: BigInteger) {
add(x shiftLeft 1)
add(x multiply Three)
add(x multiply Five)
infix fun PriorityQueue<BigInteger>.update(x: BigInteger) : PriorityQueue<BigInteger> {
add(x.shiftLeft(1))
add(x.multiply(Three))
add(x.multiply(Five))
return this
}
fun hamming(a: Any?): Any = when (a) {
is Number -> {
val pq = PriorityQueue<BigInteger>()
pq update One
val pq = PriorityQueue<BigInteger>() update One
var lowest = One
repeat(a.toInt() - 1) {
lowest = pq.poll() ?: lowest

View file

@ -0,0 +1,48 @@
import java.math.BigInteger as BI
data class LazyList<T>(val head: T, val lztail: Lazy<LazyList<T>?>) {
fun toSequence() = generateSequence(this) { it.lztail.value }
.map { it.head }
}
fun hamming(): LazyList<BI> {
fun merge(s1: LazyList<BI>, s2: LazyList<BI>): LazyList<BI> {
val s1v = s1.head; val s2v = s2.head
if (s1v < s2v) {
return LazyList(s1v, lazy({->merge(s1.lztail.value!!, s2)}))
} else {
return LazyList(s2v, lazy({->merge(s1, s2.lztail.value!!)}))
}
}
fun llmult(m: BI, s: LazyList<BI>): LazyList<BI> {
fun llmlt(ss: LazyList<BI>): LazyList<BI> {
return LazyList(m * ss.head, lazy({->llmlt(ss.lztail.value!!)}))
}
return llmlt(s)
}
fun u(s: LazyList<BI>?, n: Long): LazyList<BI> {
var r: LazyList<BI>? = null // mutable nullable so can do the below
if (s == null) { // recursively referenced variables are ugly!!!
r = llmult(BI.valueOf(n), LazyList(BI.valueOf(1), lazy{ -> r }))
} else { // recursively referenced variables only work with lazy
r = merge(s, llmult(BI.valueOf(n), // or a loop race limit
LazyList(BI.valueOf(1), lazy{ -> r })))
}
return r
}
val prms = arrayOf(5L, 3L, 2L)
val thunk = {->prms.fold<Long,LazyList<BI>?>(null, {s, n -> u(s,n)})!!}
return LazyList(BI.valueOf(1), lazy(thunk))
}
fun main(args: Array<String>) {
tailrec fun nth(n: Int, h: LazyList<BI>): BI =
if (n > 1) { nth(n - 1, h.lztail.value!!) }
else { h.head } // non-generic faster: boxing optimized away
println(hamming().toSequence().take(20).toList())
println(nth(1691, hamming()))
val strt = System.currentTimeMillis()
println(nth(1000000, hamming()))
val stop = System.currentTimeMillis()
println("Took ${stop - strt} milliseconds for the last.")
}

View file

@ -0,0 +1,24 @@
module ISet = Set.Make(struct type t = int let compare=compare end)
let pq = ref (ISet.singleton 1)
let next () =
let m = ISet.min_elt !pq in
pq := ISet.(remove m !pq |> add (2*m) |> add (3*m) |> add (5*m));
m
let () =
print_string "The first 20 are: ";
for i = 1 to 20
do
Printf.printf "%d " (next ())
done;
for i = 21 to 1690
do
ignore (next ())
done;
Printf.printf "\nThe 1691st is %d\n" (next ());

View file

@ -0,0 +1,25 @@
open Big_int
module APSet = Set.Make(
struct
type t = big_int
let compare = compare_big_int
end)
let pq = ref (APSet.singleton (big_int_of_int 1))
let next () =
let m = APSet.min_elt !pq in
let ( * ) = mult_int_big_int in
pq := APSet.(remove m !pq |> add (2*m) |> add (3*m) |> add (5*m));
m
let () =
let n = 1_000_000 in
for i = 1 to (n-1)
do
ignore (next ())
done;
Printf.printf "\nThe %dth is %s\n" n (string_of_big_int (next ()));

View file

@ -1,21 +1,21 @@
/*REXX program computes Hamming numbers: 1 ──► 20, # 1691, the one millionth.*/
numeric digits 100 /*ensure enough decimal digits. */
call hamming 1, 20 /*show the 1st ──► twentieth Hamming #s*/
call hamming 1691 /*show the 1,691st Hamming number. */
call hamming 1000000 /*show the 1 millionth Hamming number.*/
exit /*stick a fork in it, we're all done. */
/*────────────────────────────────────────────────────────────────────────────*/
/*REXX program computes Hamming numbers: 1 ──► 20, # 1691, and the one millionth. */
numeric digits 100 /*ensure enough decimal digits. */
call hamming 1, 20 /*show the 1st ──► twentieth Hamming #s*/
call hamming 1691 /*show the 1,691st Hamming number. */
call hamming 1000000 /*show the 1 millionth Hamming number.*/
exit /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
hamming: procedure; parse arg x,y; if y=='' then y=x; w=length(y)
#2=1; #3=1; #5=1; @.=0; @.1=1
do n=2 for y-1
@.n = min(2*@.#2, 3*@.#3, 5*@.#5) /*pick the minimum of 3 Hamming numbers*/
if 2*@.#2 == @.n then #2 = #2+1 /*number already defined? Use next #. */
if 3*@.#3 == @.n then #3 = #3+1 /* " " " " " " */
if 5*@.#5 == @.n then #5 = #5+1 /* " " " " " " */
end /*n*/ /* [↑] maybe assign next 3 Hamming #s.*/
do j=x to y /*W is used to align the (output) index*/
say 'Hamming('right(j,w)") =" @.j /*display 'em, Dano.*/
end /*j*/
#2=1; #3=1; #5=1; @.=0; @.1=1
do n=2 for y-1
@.n = min(2*@.#2, 3*@.#3, 5*@.#5) /*pick the minimum of 3 (Hamming) #s.*/
if 2*@.#2 == @.n then #2 = #2+1 /*number already defined? Use next #*/
if 3*@.#3 == @.n then #3 = #3+1 /* " " " " " "*/
if 5*@.#5 == @.n then #5 = #5+1 /* " " " " " "*/
end /*n*/ /* [↑] maybe assign next 3 Hamming#s*/
do j=x to y
say 'Hamming('right(j,w)") =" @.j
end /*j*/
say right( 'length of last Hamming number =' length(@.y), 70); say
return
say right( 'length of last Hamming number =' length(@.y), 70); say
return

View file

@ -1,27 +1,27 @@
/*REXX program computes Hamming numbers: 1 ──► 20, # 1691, the one millionth.*/
numeric digits 100 /*ensure enough decimal digits. */
call hamming 1, 20 /*show the 1st ──► twentieth Hamming #s*/
call hamming 1691 /*show the 1,691st Hamming number. */
call hamming 1000000 /*show the 1 millionth Hamming number.*/
exit /*stick a fork in it, we're all done. */
/*────────────────────────────────────────────────────────────────────────────*/
hamming: procedure; parse arg x,y; if y=='' then y=x; w=length(y)
#2=1; #3=1; #5=1; @.=0; @.1=1
do n=2 for y-1
_2 = @.#2 + @.#2 /*this is faster than: 2 * @.#2 */
_3 = 3 * @.#3
_5 = 5 * @.#5
m =_2 /*assume a minimum of the 3 Hamming #s.*/
if _3 < m then m =_3 /*is this number less than the minimum?*/
if _5 < m then m =_5 /* " " " " " " " */
@.n = m /*now, assign the next Hamming number. */
if _2 == m then #2 = #2 + 1 /*number already defined? Use next #. */
if _3 == m then #3 = #3 + 1 /* " " " " " " */
if _5 == m then #5 = #5 + 1 /* " " " " " " */
end /*n*/ /* [↑] maybe assign next 3 Hamming #s.*/
do j=x to y /*W is used to align the (output) index*/
say 'Hamming('right(j,w)") =" @.j /*display 'em, Dano.*/
end /*j*/
/*REXX program computes Hamming numbers: 1 ──► 20, # 1691, and the one millionth.*/
numeric digits 100 /*ensure enough decimal digits. */
call hamming 1, 20 /*show the 1st ──► twentieth Hamming #s*/
call hamming 1691 /*show the 1,691st Hamming number. */
call hamming 1000000 /*show the 1 millionth Hamming number.*/
exit /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
hamming: procedure; parse arg x,y; if y=='' then y=x; w=length(y)
#2=1; #3=1; #5=1; @.=0; @.1=1
do n=2 for y-1
_2 = @.#2 + @.#2 /*this is faster than: @.#2 * 2 */
_3 = @.#3 * 3
_5 = @.#5 * 5
m = _2 /*assume a minimum (of the 3 Hammings).*/
if _3 < m then m = _3 /*is this number less than the minimum?*/
if _5 < m then m = _5 /* " " " " " " " */
@.n = m /*now, assign the next Hamming number.*/
if _2 == m then #2 = #2 + 1 /*number already defined? Use next #.*/
if _3 == m then #3 = #3 + 1 /* " " " " " " */
if _5 == m then #5 = #5 + 1 /* " " " " " " */
end /*n*/ /* [↑] maybe assign next Hamming #'s. */
do j=x to y
say 'Hamming('right(j, w)") =" @.j
end /*j*/
say right( 'length of last Hamming number =' length(@.y), 70); say
return
say right( 'length of last Hamming number =' length(@.y), 70); say
return

View file

@ -0,0 +1,27 @@
#lang racket
(require racket/stream)
(define first stream-first)
(define rest stream-rest)
(define (hamming)
(define (merge s1 s2)
(let ([x1 (first s1)]
[x2 (first s2)])
(if (< x1 x2) ; don't have to handle duplicate case
(stream-cons x1 (merge (rest s1) s2))
(stream-cons x2 (merge s1 (rest s2))))))
(define (smult m s) ; faster than using map (* m)
(define (smlt ss)
(stream-cons (* m (first ss)) (smlt (rest ss))))
(smlt s))
(define (u s n)
(if (stream-empty? s) ; checking here more efficient than in merge
(letrec ([r (smult n (stream-cons 1 r))])
r)
(letrec ([r (merge s (smult n (stream-cons 1 r)))])
r)))
(stream-cons 1 (stream-fold u empty-stream '(5 3 2))))
(for/list ([i 20] [x (hamming)]) x) (newline)
(stream-ref (hamming) 1690) (newline)
(stream-ref (hamming) 999999) (newline)

View file

@ -0,0 +1,70 @@
extern crate num;
num::bigint::BigUint;
use std::time::Instant;
fn basic_hamming(n: usize) -> BigUint {
let two = BigUint::from(2u8);
let three = BigUint::from(3u8);
let five = BigUint::from(5u8);
let mut h = vec![BigUint::from(0u8); n];
h[0] = BigUint::from(1u8);
let mut x2 = BigUint::from(2u8);
let mut x3 = BigUint::from(3u8);
let mut x5 = BigUint::from(5u8);
let mut i = 0usize; let mut j = 0usize; let mut k = 0usize;
// BigUint comparisons are expensive, so do it only as necessary...
fn min3(x: &BigUint, y: &BigUint, z: &BigUint) -> (usize, BigUint) {
let (cs, r1) = if y == z { (0x6, y) }
else if y < z { (2, y) } else { (4, z) };
if x == r1 { (cs | 1, x.clone()) }
else if x < r1 { (1, x.clone()) } else { (cs, r1.clone()) }
}
let mut c = 1;
while c < n { // satisfy borrow checker with extra blocks: { }
let (cs, e1) = { min3(&x2, &x3, &x5) };
h[c] = e1; // vector now owns the generated value
if (cs & 1) != 0 { i += 1; x2 = &two * &h[i] }
if (cs & 2) != 0 { j += 1; x3 = &three * &h[j] }
if (cs & 4) != 0 { k += 1; x5 = &five * &h[k] }
c += 1;
}
match h.pop() {
Some(v) => v,
_ => panic!("basic_hamming: arg is zero; no elements")
}
}
fn main() {
print!("[");
for (i, h) in (1..21).map(basic_hamming).enumerate() {
if i != 0 { print!(",") }
print!(" {}", h)
}
println!(" ]");
println!("{}", basic_hamming(1691));
let strt = Instant::now();
let rslt = basic_hamming(1000000);
let elpsd = strt.elapsed();
let secs = elpsd.as_secs();
let millis = (elpsd.subsec_nanos() / 1000000)as u64;
let dur = secs * 1000 + millis;
let rs = rslt.to_str_radix(10);
let mut s = rs.as_str();
println!("{} digits:", s.len());
while s.len() > 100 {
let (f, r) = s.split_at(100);
s = r;
println!("{}", f);
}
println!("{}", s);
println!("This last took {} milliseconds", dur);
}

View file

@ -0,0 +1,32 @@
fn nodups_hamming(n: usize) -> BigUint {
let two = BigUint::from(2u8);
let three = BigUint::from(3u8);
let five = BigUint::from(5u8);
let mut m = vec![BigUint::from(0u8); 1];
m[0] = BigUint::from(1u8);
let mut h = vec![BigUint::from(0u8); n];
h[0] = BigUint::from(1u8);
if n > 1 {
m.push(BigUint::from(3u8)); // for initial x53 advance
h[1] = BigUint::from(2u8); // for initial x532 advance
}
let mut x5 = BigUint::from(5u8);
let mut x53 = BigUint::from(9u8); // 3 times 3 because already merged one step
let mut mrg = BigUint::from(3u8);
let mut x532 = BigUint::from(2u8);
let mut i = 0usize; let mut j = 1usize;
let mut c = 1usize;
while c < n { // satisfy borrow checker with extra blocks: { }
if &x532 < &mrg { h[c] = x532; i += 1; x532 = &two * &h[i]; }
else { h[c] = mrg;
if &x53 < &x5 { mrg = x53; j += 1; x53 = &three * &m[j]; }
else { mrg = x5.clone(); x5 = &five * &x5; };
m.push(mrg.clone()); };
c += 1;
}
match h.pop() {
Some(v) => v,
_ => panic!("nodups_hamming: arg is zero; no elements")
}
}

View file

@ -0,0 +1,83 @@
fn log_nodups_hamming(n: u64) -> BigUint {
if n <= 0 { panic!("nodups_hamming: arg is zero; no elements") }
if n < 2 { return BigUint::from(1u8) } // trivial case for n == 1
if n > 1.2e13 as u64 { panic!("log_nodups_hamming: argument too large to guarantee results!") }
// constants as expanded integers to minimize round-off errors, and
// reduce execution time using integer operations not float...
const LAA2: u64 = 35184372088832; // 2.0f64.powi(45)).round() as u64;
const LBA2: u64 = 55765910372219; // 3.0f64.log2() * 2.0f64.powi(45)).round() as u64;
const LCA2: u64 = 81695582054030; // 5.0f64.log2() * 2.0f64.powi(45)).round() as u64;
#[derive(Clone, Copy)]
struct Logelm { // log representation of an element with only allowable powers
exp2: u16,
exp3: u16,
exp5: u16,
logr: u64 // log representation used for comparison only - not exact
}
impl Logelm {
fn lte(&self, othr: &Logelm) -> bool {
if self.logr <= othr.logr { true } else { false }
}
fn mul2(&self) -> Logelm {
Logelm { exp2: self.exp2 + 1, logr: self.logr + LAA2, .. *self }
}
fn mul3(&self) -> Logelm {
Logelm { exp3: self.exp3 + 1, logr: self.logr + LBA2, .. *self }
}
fn mul5(&self) -> Logelm {
Logelm { exp5: self.exp5 + 1, logr: self.logr + LCA2, .. *self }
}
}
let one = Logelm { exp2: 0, exp3: 0, exp5: 0, logr: 0 };
let mut x532 = one.mul2();
let mut mrg = one.mul3();
let mut x53 = one.mul3().mul3(); // advance as mrg has the former value...
let mut x5 = one.mul5();
let mut h = Vec::with_capacity(65536); // vec!(one.clone(); 0);
let mut m = Vec::<Logelm>::with_capacity(65536); // vec!(one.clone(); 0);
let mut i = 0usize; let mut j = 0usize;
for _ in 1 .. n {
let cph = h.capacity();
if i > cph / 2 { // drain extra unneeded values...
h.drain(0 .. i);
i = 0;
}
if x532.lte(&mrg) {
h.push(x532);
x532 = h[i].mul2();
i += 1;
} else {
h.push(mrg);
if x53.lte(&x5) {
mrg = x53;
x53 = m[j].mul3();
j += 1;
} else {
mrg = x5;
x5 = x5.mul5();
}
let cpm = m.capacity();
if j > cpm / 2 { // drain extra unneeded values...
m.drain(0 .. j);
j = 0;
}
m.push(mrg);
}
}
let o = &h[&h.len() - 1];
let two = BigUint::from(2u8);
let three = BigUint::from(3u8);
let five = BigUint::from(5u8);
let mut ob = BigUint::from(1u8); // convert to BigUint at the end
for _ in 0 .. o.exp2 { ob = ob * &two }
for _ in 0 .. o.exp3 { ob = ob * &three }
for _ in 0 .. o.exp5 { ob = ob * &five }
ob
}

View file

@ -0,0 +1,131 @@
extern crate num; // requires dependency on the num library
use num::bigint::BigUint;
use std::time::Instant;
fn log_nodups_hamming_iter() -> Box<Iterator<Item = (u16, u16, u16)>> {
// constants as expanded integers to minimize round-off errors, and
// reduce execution time using integer operations not float...
const LAA2: u64 = 35184372088832; // 2.0f64.powi(45)).round() as u64;
const LBA2: u64 = 55765910372219; // 3.0f64.log2() * 2.0f64.powi(45)).round() as u64;
const LCA2: u64 = 81695582054030; // 5.0f64.log2() * 2.0f64.powi(45)).round() as u64;
#[derive(Clone, Copy)]
struct Logelm { // log representation of an element with only allowable powers
exp2: u16,
exp3: u16,
exp5: u16,
logr: u64 // log representation used for comparison only - not exact
}
impl Logelm {
fn lte(&self, othr: &Logelm) -> bool {
if self.logr <= othr.logr { true } else { false }
}
fn mul2(&self) -> Logelm {
Logelm { exp2: self.exp2 + 1, logr: self.logr + LAA2, .. *self }
}
fn mul3(&self) -> Logelm {
Logelm { exp3: self.exp3 + 1, logr: self.logr + LBA2, .. *self }
}
fn mul5(&self) -> Logelm {
Logelm { exp5: self.exp5 + 1, logr: self.logr + LCA2, .. *self }
}
}
let one = Logelm { exp2: 0, exp3: 0, exp5: 0, logr: 0 };
let mut x532 = one.mul2();
let mut mrg = one.mul3();
let mut x53 = one.mul3().mul3(); // advance as mrg has the former value...
let mut x5 = one.mul5();
let mut h = Vec::with_capacity(65536);
let mut m = Vec::<Logelm>::with_capacity(65536);
let mut i = 0usize; let mut j = 0usize;
Box::new((0u64 .. ).map(move |it| if it < 1 { (0, 0, 0) } else {
let cph = h.capacity();
if i > cph / 2 {
h.drain(0 .. i);
i = 0;
}
if x532.lte(&mrg) {
h.push(x532);
x532 = h[i].mul2();
i += 1;
} else {
h.push(mrg);
if x53.lte(&x5) {
mrg = x53;
x53 = m[j].mul3();
j += 1;
} else {
mrg = x5;
x5 = x5.mul5();
}
let cpm = m.capacity();
if j > cpm / 2 {
m.drain(0 .. j);
j = 0;
}
m.push(mrg);
}
let o = &h[&h.len() - 1];
(o.exp2, o.exp3, o.exp5)
}))
}
fn convert_log2big(o: (u16, u16, u16)) -> BigUint {
let two = BigUint::from(2u8);
let three = BigUint::from(3u8);
let five = BigUint::from(5u8);
let (x2, x3, x5) = o;
let mut ob = BigUint::from(1u8); // convert to BigUint at the end
for _ in 0 .. x2 { ob = ob * &two }
for _ in 0 .. x3 { ob = ob * &three }
for _ in 0 .. x5 { ob = ob * &five }
ob
}
fn main() {
print!("[");
for (i, h) in log_nodups_hamming_iter().take(20).map(convert_log2big).enumerate() {
if i != 0 { print!(",") }
print!(" {}", h)
}
println!(" ]");
println!("{}", convert_log2big(log_nodups_hamming_iter().take(1691).last().unwrap()));
let strt = Instant::now();
// let rslt = convert_log2big(log_nodups_hamming_iter().take(1000000000).last().unwrap());
let mut it = log_nodups_hamming_iter().into_iter();
for _ in 0 .. 100-1 { // a little faster; less one level of iteration
let _ = it.next();
}
let rslt = convert_log2big(it.next().unwrap());
let elpsd = strt.elapsed();
let secs = elpsd.as_secs();
let millis = (elpsd.subsec_nanos() / 1000000)as u64;
let dur = secs * 1000 + millis;
println!("2^{} times 3^{} times 5^{}", rslt.0, rslt.1, rslt.2);
let rs = convert_log2big(rslt).to_str_radix(10);
let mut s = rs.as_str();
println!("{} digits:", s.len());
let lg3 = 3.0f64.log2();
let lg5 = 5.0f64.log2();
let lg = (rslt.0 as f64 + rslt.1 as f64 * lg3
+ rslt.2 as f64 * lg5) * 2.0f64.log10();
println!("Approximately {}E+{}", 10.0f64.powf(lg.fract()), lg.trunc());
if s.len() <= 10000 {
while s.len() > 100 {
let (f, r) = s.split_at(100);
s = r;
println!("{}", f);
}
println!("{}", s);
}
println!("This last took {} milliseconds.", dur);
}

View file

@ -0,0 +1,255 @@
extern crate num;
use num::bigint::BigUint;
use std::rc::Rc;
use std::iter::FromIterator;
use std::cell::{UnsafeCell, RefCell};
use std::mem;
use std::time::Instant;
// since Box<FnOnce() -> T + 'a> doesn't currently work and
// FnBox, which does work, (version 1.13) is UnStable;
// use the boilerplate Invoke trait and Thunk
// from the old removed thunk standard library...
pub trait Invoke<R = ()> {
fn invoke(self: Box<Self>) -> R;
}
impl<R, F: FnOnce() -> R> Invoke<R> for F {
#[inline(always)]
fn invoke(self: Box<F>) -> R { (*self)() }
}
pub struct Thunk<'a, R>(Box<Invoke<R> + 'a>);
impl<'a, R: 'a> Thunk<'a, R> {
#[inline(always)]
fn new<F: 'a + FnOnce() -> R>(func: F) -> Thunk<'a, R> {
Thunk(Box::new(func))
}
#[inline(always)]
fn invoke(self) -> R { self.0.invoke() }
}
// actual Lazy implementation starts here...
use self::LazyState::*;
pub struct Lazy<'a, T: 'a>(UnsafeCell<LazyState<'a, T>>);
enum LazyState<'a, T: 'a> {
Unevaluated(Thunk<'a, T>),
EvaluationInProgress,
Evaluated(T)
}
impl<'a, T: 'a> Lazy<'a, T>{
#[inline]
pub fn new<'b, F>(thunk: F) -> Lazy<'b, T>
where F: 'b + FnOnce() -> T {
Lazy(UnsafeCell::new(Unevaluated(Thunk::new(thunk))))
}
#[inline]
pub fn evaluated(val: T) -> Lazy<'a, T> {
Lazy(UnsafeCell::new(Evaluated(val)))
}
#[inline]
fn force<'b>(&'b self) { // not thread-safe
unsafe {
match *self.0.get() {
Evaluated(_) => return, // nothing required; already Evaluated
EvaluationInProgress => panic!("Lazy::force called recursively!!!"),
_ => () // need to do following something else if Unevaluated...
} // following eliminates recursive race; drops neither on replace...
match mem::replace(&mut *self.0.get(), EvaluationInProgress) {
Unevaluated(thnk) => { // thnk can't call force on the same Lazy
*self.0.get() = Evaluated(thnk.invoke());
},
_ => unreachable!() // already took care of other cases in above match.
}
}
}
#[inline]
pub fn value<'b>(&'b self) -> &'b T {
self.force(); // evaluatate if not evealutated
match unsafe { &*self.0.get() } {
&Evaluated(ref v) => v, // return value
_ => { unreachable!() } // previous force guarantees never not Evaluated
}
}
#[inline]
pub fn unwrap<'b>(self) -> T where T: 'b { // consumes the object to produce the value
self.force(); // evaluatate if not evealutated
match unsafe { self.0.into_inner() } {
Evaluated(v) => v,
_ => unreachable!() // previous code guarantees never not Evaluated
}
}
}
// now for immutable persistent (memoized) LazyList via Lazy above
type RcLazyListNode<'a, T: 'a> = Rc<Lazy<'a, LazyList<'a, T>>>;
use self::LazyList::*;
#[derive(Clone)]
enum LazyList<'a, T: 'a + Clone> {
/// The Empty List
Empty,
/// A list with one member and possibly another list.
Cons(T, RcLazyListNode<'a, T>)
}
impl<'a, T: 'a + Clone> LazyList<'a, T> {
#[inline]
pub fn cons<F>(v: T, cntf: F) -> LazyList<'a, T>
where F: 'a + FnOnce() -> LazyList<'a, T> {
Cons(v, Rc::new(Lazy::new(cntf)))
}
#[inline]
pub fn head<'b>(&'b self) -> &'b T {
if let Cons(ref hd, _) = *self { return hd }
panic!("LazyList::head called on an Empty LazyList!!!")
}
#[inline]
pub fn tail<'b>(&'b self) -> &'b Lazy<'a, LazyList<'a, T>> {
if let Cons(_, ref rlln) = *self { return &*rlln }
panic!("LazyList::tail called on an Empty LazyList!!!")
}
#[inline]
pub fn unwrap(self) -> (T, RcLazyListNode<'a, T>) { // consumes the object
if let Cons(hd, rlln) = self {
return (hd, rlln) }
panic!("LazyList::unwrap called on an Empty LazyList!!!")
}
}
impl<'a, T: 'a + Clone> Iterator for LazyList<'a, T> {
type Item = T;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if let Empty = *self { return None }
let oldll = mem::replace(self, Empty);
let (hd, rlln) = oldll.unwrap();
let mut newll = rlln.value().clone();
mem::swap(self, &mut newll); // self now contains tail, newll contains the Empty
Some(hd)
}
}
// implements worker wrapper recursion closures using shared RcMFn variable...
type RcMFn<'a, T: 'a> = Rc<UnsafeCell<Box<FnMut(T) -> T + 'a>>>;
//#[derive(Clone)]
//struct RcMFn<'a, T: 'a>(Rc<UnsafeCell<Box<FnMut() -> T + 'a>>>);
trait RcMFnMethods<'a, T> {
fn create<F: FnMut(T) -> T + 'a>(v: F) -> RcMFn<'a, T>;
fn invoke(&self, v: T) -> T;
fn set<F: FnMut(T) -> T + 'a>(&self, v: F);
}
impl<'a, T: 'a> RcMFnMethods<'a, T> for RcMFn<'a, T> {
fn create<F: FnMut(T) -> T + 'a>(v: F) -> RcMFn<'a, T> { // creates new value wrapper
Rc::new(UnsafeCell::new(Box::new(v)))
}
#[inline(always)] // needs to be faster to be worth it
fn invoke(&self, v: T) -> T {
unsafe { (*(*(*self).get()))(v) }
}
fn set<F: FnMut(T) -> T + 'a>(&self, v: F) {
unsafe { *self.get() = Box::new(v); }
}
}
// implementation for a reference-counted, interior-mutable variable
// necessary for such things as sharing data and recursive variables
type RcMVar<T> = Rc<RefCell<T>>;
//#[derive(Clone)]
//struct RcMVar<T>(Rc<RefCell<T>>);
trait RcMVarMethods<T> {
fn create(v: T) -> Self;
fn get(self: &Self) -> T;
fn set(self: &Self, v: T);
}
impl<T: Clone> RcMVarMethods<T> for RcMVar<T> {
fn create(v: T) -> RcMVar<T> { // creates new value wrapped in RcMVar
Rc::new(RefCell::new(v))
}
#[inline]
fn get(&self) -> T {
self.borrow().clone()
}
fn set(&self, v: T) {
*self.borrow_mut() = v;
}
}
fn hammings() -> Box<Iterator<Item = Rc<BigUint>>> {
type LL<'a> = LazyList<'a, Rc<BigUint>>;
fn merge<'a>(x: LL<'a>, y: LL<'a>) -> LL<'a> {
let lte = { x.head() <= y.head() }; // private context for borrow
if lte {
let (hdx, tlx) = x.unwrap();
LL::cons(hdx, move || merge(tlx.value().clone(), y))
} else {
let (hdy, tly) = y.unwrap();
LL::cons(hdy, move || merge(x, tly.value().clone()))
}
}
fn smult<'a>(m: BigUint, s: LL<'a>) -> LL<'a> { // like map m * but faster...
let smlt = RcMFn::create(move |ss: LL<'a>| ss);
let csmlt = smlt.clone();
smlt.set(move |ss: LL<'a>| {
let (hd, tl) = ss.unwrap();
let ccsmlt = csmlt.clone();
LL::cons(Rc::new(&m * &*hd), move || ccsmlt.invoke(tl.value().clone()))
});
smlt.invoke(s)
}
fn u<'a>(s: LL<'a>, n: usize) -> LL<'a> {
let nb = BigUint::from(n);
let rslt = RcMVar::create(Empty);
let crslt = rslt.clone(); // same interior data...
let cll = LL::cons(Rc::new(BigUint::from(1u8)), move || crslt.get()); // gets future value
// below sets future value for above closure...
rslt.set(if let Empty = s { smult(nb, cll) } else { merge(s, smult(nb, cll)) });
rslt.get()
}
fn rll<'a>() -> LL<'a> { [5, 3, 2].into_iter()
.fold(Empty, |ll, n| u(ll, *n) ) }
let hmng = LL::cons(Rc::new(BigUint::from(1u8)), move || rll());
Box::new(hmng.into_iter())
}
fn main() {
print!("[");
for (i, h) in hammings().take(20).enumerate() {
if i != 0 { print!(",") }
print!(" {}", h)
}
println!(" ]");
println!("{}", hammings().take(1691).last().unwrap());
let strt = Instant::now();
let rslt = hammings().take(1000000).last().unwrap();
let elpsd = strt.elapsed();
let secs = elpsd.as_secs();
let millis = (elpsd.subsec_nanos() / 1000000)as u64;
let dur = secs * 1000 + millis;
println!("{}", rslt);
println!("This last took {} milliseconds.", dur);
}

View file

@ -0,0 +1,95 @@
extern crate num; // requires dependency on the num library
use num::bigint::BigUint;
use std::time::Instant;
fn nth_hamming(n: u64) -> (u32, u32, u32) {
if n < 2 {
if n <= 0 { panic!("nth_hamming: argument is zero; no elements") }
return (0, 0, 0) // trivial case for n == 1
}
let lg3 = 3.0f64.ln() / 2.0f64.ln(); // log base 2 of 3
let lg5 = 5.0f64.ln() / 2.0f64.ln(); // log base 2 of 5
let fctr = 6.0f64 * lg3 * lg5;
let crctn = 30.0f64.sqrt().ln() / 2.0f64.ln(); // log base 2 of sqrt 30
let lgest = (fctr * n as f64).powf(1.0f64/3.0f64)
- crctn; // from WP formula
let frctn = if n < 1000000000 { 0.509f64 } else { 0.105f64 };
let lghi = (fctr * (n as f64 + frctn * lgest)).powf(1.0f64/3.0f64)
- crctn; // calculate hi log limit based on log(N) - WP article
let lglo = 2.0f64 * lgest - lghi; // and a lower limit of the upper "band"
let mut count = 0; // need to use extended precision, might go over
let mut bnd = Vec::with_capacity(0);
let klmt = (lghi / lg5) as u32 + 1;
for k in 0 .. klmt { // i, j, k values can be just u32 values
let p = k as f64 * lg5;
let jlmt = ((lghi - p) / lg3) as u32 + 1;
for j in 0 .. jlmt {
let q = p + j as f64 * lg3;
let ir = lghi - q;
let lg = q + (ir as u32) as f64; // current log value (estimated)
count += ir as u64 + 1;
if lg >= lglo {
bnd.push((lg, (ir as u32, j, k)))
}
}
}
if n > count { panic!("nth_hamming: band high estimate is too low!") };
let ndx = (count - n) as usize;
if ndx >= bnd.len() { panic!("nth_hamming: band low estimate is too high!") };
bnd.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap()); // sort decreasing order
bnd[ndx].1
}
fn convert_log2big(o: (u32, u32, u32)) -> BigUint {
let two = BigUint::from(2u8);
let three = BigUint::from(3u8);
let five = BigUint::from(5u8);
let (x2, x3, x5) = o;
let mut ob = BigUint::from(1u8); // convert to BigUint at the end
for _ in 0 .. x2 { ob = ob * &two }
for _ in 0 .. x3 { ob = ob * &three }
for _ in 0 .. x5 { ob = ob * &five }
ob
}
fn main() {
print!("[");
for (i, h) in (1 .. 21).map(nth_hamming).enumerate() {
if i != 0 { print!(",") }
print!(" {}", convert_log2big(h))
}
println!(" ]");
println!("{}", convert_log2big(nth_hamming(1691)));
let strt = Instant::now();
let rslt = nth_hamming(1000000);
let elpsd = strt.elapsed();
let secs = elpsd.as_secs();
let millis = (elpsd.subsec_nanos() / 1000000)as u64;
let dur = secs * 1000 + millis;
println!("2^{} times 3^{} times 5^{}", rslt.0, rslt.1, rslt.2);
let rs = convert_log2big(rslt).to_str_radix(10);
let mut s = rs.as_str();
println!("{} digits:", s.len());
let lg3 = 3.0f64.log2();
let lg5 = 5.0f64.log2();
let lg = (rslt.0 as f64 + rslt.1 as f64 * lg3
+ rslt.2 as f64 * lg5) * 2.0f64.log10();
println!("Approximately {}E+{}", 10.0f64.powf(lg.fract()), lg.trunc());
if s.len() <= 10000 {
while s.len() > 100 {
let (f, r) = s.split_at(100);
s = r;
println!("{}", f);
}
println!("{}", s);
}
println!("This last took {} milliseconds.", dur);
}

View file

@ -1,13 +1,12 @@
def hamming(): Stream[BigInt] = {
def merge(a: Stream[BigInt], b: Stream[BigInt]): Stream[BigInt] = {
val av = a.head; val bv = b.head
if (av < bv) av #:: merge(a.tail, b)
else bv #:: merge(a, b.tail)
}
def smult(m:BigInt, s: Stream[BigInt]): Stream[BigInt] =
(m * s.head) #:: smult(m, s.tail) // equiv to map (m *) s - faster
lazy val s5: Stream[BigInt] = 5 #:: smult(5, s5)
lazy val s35: Stream[BigInt] = 3 #:: merge(s5, smult(3, s35))
lazy val s235: Stream[BigInt] = 2 #:: merge(s35, smult(2, s235))
1 #:: s235
}
if (a.isEmpty) b else {
val av = a.head; val bv = b.head
if (av < bv) av #:: merge(a.tail, b)
else bv #:: merge(a, b.tail) } }
def smult(m:Int, s: Stream[BigInt]): Stream[BigInt] =
(m * s.head) #:: smult(m, s.tail) // equiv to map (m *) s; faster
def u(s: Stream[BigInt], n: Int): Stream[BigInt] = {
lazy val r: Stream[BigInt] = merge(s, smult(n, 1 #:: r))
r }
1 #:: List(5, 3, 2).foldLeft(Stream.empty[BigInt]) { u } }

View file

@ -1,14 +1,17 @@
(define (hamming)
(define (foldl f z l)
(define (foldls zs ls)
(if (null? ls) zs (foldls (f zs (car ls)) (cdr ls))))
(foldls z l))
(define (merge a b)
(let ((x (car a)) (y (car b)))
(if (< x y) (cons x (delay (merge (force (cdr a)) b)))
(cons y (delay (merge a (force (cdr b))))))))
(define (smult m s) (cons (* m (car s))
(delay (smult m (force (cdr s)))))) ;; equiv to map (* m) s
(define s5 (cons 5 (delay (smult 5 s5))))
(define s35 (cons 3 (delay (merge s5 (smult 3 s35)))))
(define s235 (cons 2 (delay (merge s35 (smult 2 s235)))))
(cons 1 (delay s235)))
(if (null? a) b
(let ((x (car a)) (y (car b)))
(if (< x y) (cons x (delay (merge (force (cdr a)) b)))
(cons y (delay (merge a (force (cdr b)))))))))
(define (smult m s) (cons (* m (car s)) ;; equiv to map (* m) s; faster
(delay (smult m (force (cdr s))))))
(define (u s n) (letrec ((a (merge s (smult n (cons 1 (delay a)))))) a))
(cons 1 (delay (foldl u '() '(5 3 2)))))
;;; test...
(define (stream-take->list n strm)

View file

@ -0,0 +1,17 @@
10 FOR h=1 TO 20: GO SUB 1000: NEXT h
20 LET h=1691: GO SUB 1000
30 STOP
1000 REM Hamming
1010 DIM a(h)
1030 LET a(1)=1: LET x2=2: LET x3=3: LET x5=5: LET i=1: LET j=1: LET k=1
1040 FOR n=2 TO h
1050 LET m=x2
1060 IF m>x3 THEN LET m=x3
1070 IF m>x5 THEN LET m=x5
1080 LET a(n)=m
1090 IF m=x2 THEN LET i=i+1: LET x2=2*a(i)
1100 IF m=x3 THEN LET j=j+1: LET x3=3*a(j)
1110 IF m=x5 THEN LET k=k+1: LET x5=5*a(k)
1120 NEXT n
1130 PRINT "H(";h;")= ";a(h)
1140 RETURN