Binary pebbling

In [1]:
# Showcasing recursive binary pebblers: rushing, speed-1, speed-2, optimal.
# Copyright (C) 2016 Berry Schoenmakers
import hashlib, itertools

tR=lambda k,r: 0 if r<2**k-1 else 2**k-1
t1=lambda k,r: 1
t2=lambda k,r: 0 if r<2**(k-1) else 2 if r<2**k-1 else 1
tS=lambda k,r: 0 if r<2**(k-1) else ((k+r)%2+k+1-((2*r)%(2**(2**k-r).bit_length())).bit_length())//2

def P(k, x):
    y=[None]*k+[x]
    i=k; g=0
    for r in range(1,1<<k):
        for _ in range(t(k,r)):
            z=y[i]
            if g==0: i-=1; g=1<<i
            y[i]=hashlib.sha256(z).digest()
            g-=1
        yield
    yield y[0]
    for v in itertools.zip_longest(*(P(i-1,y[i]) for i in range(1,k+1))):
        yield next(filter(None, v))
In [2]:
t=t2
k=3
x=hashlib.sha256(b'').digest()
for v in P(k,x): 
    if v: print(v.hex())
30edbc354e8cf656adcddbeefbf3f5073372cdc42e4eca2e797bda8abebb6a05
06adb8ba851d855951490003c3c1fe4d3a2e6cd87a1299208ddfd3949d131657
1bee5a29daf06786a5516d9226b30bbdbf422a7ed475e42c3f930ea04c612091
9ff42bc5a042d3c79a61d7cb6769408fe13b1e5fc30e01e19a100e79109ed688
75d7682c8b5955557b2ef33654f31512b9b3edd17f74b5bf422ccabbd7537e1a
aa6ac2d4961882f42a345c7615f4133dde8e6d6e7c1b6b40ae4ff6ee52c393d0
5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

Using natural numbers without subtraction instead of one-way hashes:

In [3]:
def Pn(k, x):
    y=[None]*k+[x]
    i=k; g=0
    for r in range(1,1<<k):
        for _ in range(t(k,r)):
            z=y[i]
            if g==0: i-=1; g=1<<i
            y[i]=z+1 #hashlib.sha256(z).digest()
            g-=1
        yield
    yield y[0]
    for v in itertools.zip_longest(*(Pn(i-1,y[i]) for i in range(1,k+1))):
        yield next(filter(None, v))
In [4]:
t=tS
k=3
x=1 #hashlib.sha256(b'').digest()
for v in Pn(k,x): 
    if v: print(v) #print(v.hex())
8
7
6
5
4
3
2
1

Measuring time and space utilization per round.

T = number of hashes    
S = number of hash values stored
In [5]:
def Pm(k,t):
    i=k; g=0
    for r in range(1,2**k):
        yield (t(k,r),k-i+1)
        for _ in range(t(k,r)):
            if g==0: i-=1; g=2**i
            g-=1
    yield (0,k+1)
    for v in itertools.zip_longest(*(Pm(i-1,t) for i in range(1,k+1))):
        (a,b)=zip(*(u for u in v if u!=None))
        yield (sum(a),sum(b))
In [6]:
k=3
print("   k:{0:2}    rushing   speed-1   speed-2   optimal".format(k))
print("round r      T  S      T  S      T  S      T  S ")
r=1
for ((wR,sR),(w1,s1),(w2,s2),(wS,sS)) in zip(*(Pm(k,t) for t in (tR,t1,t2,tS))):
    if r==2**k: wR=w1=w2=wS="      W"
    print("{0:7}{1:7}{2:3}{3:7}{4:3}{5:7}{6:3}{7:7}{8:3}".format(r,wR,sR,w1,s1,w2,s2,wS,sS))
    r+=1   
   k: 3    rushing   speed-1   speed-2   optimal
round r      T  S      T  S      T  S      T  S 
      1      0  1      1  1      0  1      0  1
      2      0  1      1  2      0  1      0  1
      3      0  1      1  2      0  1      0  1
      4      0  1      1  2      2  1      2  1
      5      0  1      1  2      2  2      1  2
      6      0  1      1  3      2  2      2  2
      7      7  1      1  3      1  3      2  3
      8      W  4      W  4      W  4      W  4
      9      1  3      2  3      1  3      1  3
     10      0  3      1  4      2  3      1  3
     11      3  2      1  3      1  3      2  3
     12      0  3      0  3      0  3      0  3
     13      1  2      1  2      1  2      1  2
     14      0  2      0  2      0  2      0  2
     15      0  1      0  1      0  1      0  1