[CentralOH] 2017-10-20 燕月 Lunch Placemat Scribbles: tcl braces versus python; fibonacci versus hamming
jep200404 at columbus.rr.com
jep200404 at columbus.rr.com
Fri Oct 20 22:24:22 EDT 2017
more code than usual
lunch:
tcl braces
versus python
fibonacci and hamming numbers
tcl without braces is painfully slow
doj at sbc:~/20171026$ cat FibForXNoBraces.tcl
proc Fib {n} {
if {$n == 0} {
set Ret_Value 0
} else {
if {$n < 3} {
set Ret_Value 1
} else {
set PrevVal 1
set ThisVal 1
for {set y 3} {$y <= $n} {incr y} {
set Ret_Value [expr $PrevVal + $ThisVal]
set PrevVal $ThisVal
set ThisVal $Ret_Value
}
}
}
return $Ret_Value
}
puts "Please enter seed value:"
gets stdin x
set StartTime [clock microseconds]
set Answer [Fib $x]
set EndTime [clock microseconds]
puts "Seed $x; Result $Answer"
puts "Duration [expr $EndTime - $StartTime] microseconds"
doj at sbc:~/20171026$ echo 11111 | tclsh FibForXNoBraces.tcl
Please enter seed value:
Seed 11111; Result 515...489
Duration 129277783 microseconds
doj at sbc:~/20171026$
tcl with braces is fast
doj at sbc:~/20171026$ cat FibForXWithBraces.tcl
proc Fib {n} {
if {$n == 0} {
set Ret_Value 0
} else {
if {$n < 3} {
set Ret_Value 1
} else {
set PrevVal 1
set ThisVal 1
for {set y 3} {$y <= $n} {incr y} {
set Ret_Value [expr {$PrevVal + $ThisVal}]
set PrevVal $ThisVal
set ThisVal $Ret_Value
}
}
}
return $Ret_Value
}
puts "Please enter seed value:"
gets stdin x
set StartTime [clock microseconds]
set Answer [Fib $x]
set EndTime [clock microseconds]
puts "Seed $x; Result $Answer"
puts "Duration [expr $EndTime - $StartTime] microseconds"
doj at sbc:~/20171026$ diff FibForXNoBraces.tcl FibForXWithBraces.tcl
12c12
< set Ret_Value [expr $PrevVal + $ThisVal]
---
> set Ret_Value [expr {$PrevVal + $ThisVal}]
doj at sbc:~/20171026$ echo 11111 | tclsh FibForXWithBraces.tcl
Please enter seed value:
Seed 11111; Result 515...489
Duration 42093 microseconds
doj at sbc:~/20171026$
regular Python is faster
(jupy) doj at sbc:~/20171026$ cat fib.py
#!/usr/bin/env python3
import sys
from timeit import timeit
source_code = '''
def fib(n):
if n == 0:
return 0
a, b = 0, 1
for _ in range(n):
a, b = b, a + b
return a
'''
exec(source_code)
def main():
for arg in sys.argv[1:]:
n = int(arg)
number = 1000
elapsed_time = timeit('fib(%s)' % n, setup=source_code, number=number)
print(
f'{elapsed_time}s total / {number} loops -> '
f'{elapsed_time / number}s per loop for fib({n}) -> {fib(n)}'
)
if __name__ == '__main__':
main()
(jupy) doj at sbc:~/20171026$ ./fib.py 0 1 2 3 4 5 6 7 11111
0.00038148299790918827s total / 1000 loops -> 3.814829979091883e-07s per loop for fib(0) -> 0
0.0018241599900647998s total / 1000 loops -> 1.8241599900647999e-06s per loop for fib(1) -> 1
0.0020077070221304893s total / 1000 loops -> 2.0077070221304894e-06s per loop for fib(2) -> 1
0.002059810038190335s total / 1000 loops -> 2.059810038190335e-06s per loop for fib(3) -> 2
0.0022625650162808597s total / 1000 loops -> 2.26256501628086e-06s per loop for fib(4) -> 3
0.0025070849806070328s total / 1000 loops -> 2.5070849806070328e-06s per loop for fib(5) -> 5
0.0026626960025168955s total / 1000 loops -> 2.6626960025168955e-06s per loop for fib(6) -> 8
0.002945350017398596s total / 1000 loops -> 2.945350017398596e-06s per loop for fib(7) -> 13
8.536355245974846s total / 1000 loops -> 0.008536355245974845s per loop for fib(11111) -> 515...489
(jupy) doj at sbc:~/20171026$
pypy is faster yet.
This is the first time that I have used pypy.
(jupy) doj at sbc:~/20171026$ cat fibpypy.py
#!/usr/bin/env pypy
import sys
from timeit import timeit
source_code = '''
def fib(n):
if n == 0:
return 0
a, b = 0, 1
for _ in range(n):
a, b = b, a + b
return a
'''
exec(source_code)
def main():
for arg in sys.argv[1:]:
n = int(arg)
number = 1000
elapsed_time = timeit('fib(%s)' % n, setup=source_code, number=number)
print(
'{0}s total / {1} loops -> '
'{2}s per loop for fib({3}) -> {4}'.format(
elapsed_time,
number,
elapsed_time / number,
n,
fib(n),
)
)
if __name__ == '__main__':
main()
(jupy) doj at sbc:~/20171026$ diff fib.py fibpypy.py
1c1
< #!/usr/bin/env python3
---
> #!/usr/bin/env pypy
24,25c24,31
< f'{elapsed_time}s total / {number} loops -> '
< f'{elapsed_time / number}s per loop for fib({n}) -> {fib(n)}'
---
> '{0}s total / {1} loops -> '
> '{2}s per loop for fib({3}) -> {4}'.format(
> elapsed_time,
> number,
> elapsed_time / number,
> n,
> fib(n),
> )
(jupy) doj at sbc:~/20171026$ ./fibpypy.py 0 1 2 3 4 5 6 7 11111
0.00112700462341s total / 1000 loops -> 1.12700462341e-06s per loop for fib(0) -> 0
0.00417494773865s total / 1000 loops -> 4.17494773865e-06s per loop for fib(1) -> 1
0.00802302360535s total / 1000 loops -> 8.02302360535e-06s per loop for fib(2) -> 1
0.00941896438599s total / 1000 loops -> 9.41896438599e-06s per loop for fib(3) -> 2
0.0132009983063s total / 1000 loops -> 1.32009983063e-05s per loop for fib(4) -> 3
0.011430978775s total / 1000 loops -> 1.1430978775e-05s per loop for fib(5) -> 5
0.0093469619751s total / 1000 loops -> 9.3469619751e-06s per loop for fib(6) -> 8
0.00933718681335s total / 1000 loops -> 9.33718681335e-06s per loop for fib(7) -> 13
3.84163093567s total / 1000 loops -> 0.00384163093567s per loop for fib(11111) -> 515...489
(jupy) doj at sbc:~/20171026$
It had been a long time since I had looked at tcl.
I think a big virtue of tcl is its smallness.
###############################################################################
making hamming numbers
wp:Smooth number
wp:Hamming numbers
wp:Richard Hamming
wp: prefix means Wikipedia
To get good answers, consider following the advice in the links below.
http://catb.org/~esr/faqs/smart-questions.html
http://web.archive.org/web/20090627155454/www.greenend.org.uk/rjk/2000/06/14/quoting.html
My first version is correct, but gobbles memory like crazy,
much much worse than a naive recursive fibonacci function.
It starts swapping soon, at which point all hope is lost.
doj at sbc:~/20171026$ cat ham1.py
#!/usr/bin/env python3
import sys
from itertools import islice
def ham(x=1):
yield x
g2 = ham(2 * x)
g3 = ham(3 * x)
g5 = ham(5 * x)
x2 = next(g2)
x3 = next(g3)
x5 = next(g5)
while True:
x = min(x2, x3, x5)
yield x
if x2 == x:
x2 = next(g2)
if x3 == x:
x3 = next(g3)
if x5 == x:
x5 = next(g5)
def nth_hamming_number(n):
return list(islice(ham(), n-1, n))[0]
def main():
for arg in sys.argv[1:]:
n = int(arg)
x = nth_hamming_number(n)
print(n, x)
if __name__ == '__main__':
main()
doj at sbc:~/20171026$
doj at sbc:~/20171026$ time ./ham1.py 1
1 1
real 0m0.057s
user 0m0.044s
sys 0m0.012s
doj at sbc:~/20171026$ time ./ham1.py 10
10 12
real 0m0.058s
user 0m0.048s
sys 0m0.012s
doj at sbc:~/20171026$ time ./ham1.py 100
100 1536
real 0m0.087s
user 0m0.072s
sys 0m0.012s
doj at sbc:~/20171026$ time ./ham1.py 200
200 16200
real 0m0.429s
user 0m0.384s
sys 0m0.044s
doj at sbc:~/20171026$ time ./ham1.py 300
300 82944
real 0m2.950s
user 0m2.696s
sys 0m0.244s
doj at sbc:~/20171026$ time ./ham1.py 400
400 311040
real 0m12.706s
user 0m11.792s
sys 0m0.884s
doj at sbc:~/20171026$ time ./ham1.py 500
500 937500
real 0m41.093s
user 0m38.336s
sys 0m2.672s
doj at sbc:~/20171026$ time ./ham1.py 600
this was killed after a long time of swapping
Second version tries to avoid duplication,
so is much faster and uses much much less memory.
Seems to be fast enough for the 1000000th hamming number.
It even seems to be correct.
I wonder about using a decorator to avoid having the global dictionary.
doj at sbc:~/20171026$ cat ham2.py
#!/usr/bin/env python3
import sys
from itertools import islice
already_done = set()
def ham(x=1):
global already_done
if x in already_done:
return
already_done |= {x}
yield x
x_from_generators = {}
generators = (ham(prime * x) for prime in (2, 3, 5))
for g in generators:
try:
x_from_generators[g] = next(g)
except StopIteration:
pass
while x_from_generators:
x_min = min(x_from_generators.values())
yield x_min
for g, x in x_from_generators.items():
if x != x_min:
continue
try:
x_from_generators[g] = next(g)
except StopIteration:
x_from_generators.pop(g)
def nth_hamming_number(n):
return list(islice(ham(), n-1, n))[0]
def main():
for arg in sys.argv[1:]:
n = int(arg)
x = nth_hamming_number(n)
print(n, x)
if __name__ == '__main__':
main()
doj at sbc:~/20171026$
doj at sbc:~/20171026$ time ./ham2.py 1
1 1
real 0m0.055s
user 0m0.036s
sys 0m0.020s
doj at sbc:~/20171026$ time ./ham2.py 10
10 12
real 0m0.057s
user 0m0.048s
sys 0m0.004s
doj at sbc:~/20171026$ time ./ham2.py 100
100 1536
real 0m0.061s
user 0m0.048s
sys 0m0.012s
doj at sbc:~/20171026$ time ./ham2.py 1000
1000 51200000
real 0m0.111s
user 0m0.080s
sys 0m0.028s
doj at sbc:~/20171026$ time ./ham2.py 10000
10000 288325195312500000
real 0m1.115s
user 0m1.080s
sys 0m0.032s
doj at sbc:~/20171026$ time ./ham2.py 100000
100000 290142196707511001929482240000000000000
real 0m23.579s
user 0m23.332s
sys 0m0.200s
doj at sbc:~/20171026$ time ./ham2.py 1000000
1000000 519312780448388736089589843750000000000000000000000000000000000000000000000000000000
real 8m31.437s
user 8m27.944s
sys 0m2.252s
doj at sbc:~/20171026$
More information about the CentralOH
mailing list