Extend New Binary Sequence ?

Dean Hickerson dean at math.ucdavis.edu
Wed Sep 5 01:48:48 CEST 2007


David Wilson wrote:

> If you use the recurrence
>
>     [1] a(n) = a(n-1) + a([n/2])
>
> to compute A000123(n), you must at some point compute a(n-1), then
> a(n-2), etc, all the way down to 0. This means that any algorithm based
> on this recurrence (including those based on the generating function of
> A000123, which is essential equivalent) takes O(n) time to compute a(n).
> The only way to avoid this is to use a better formula or recurrence
> (such as a duplication formula).
>
> Unfortunately, in the case of A000123, I don't think we have such a
> formula. A000123 is a partition-like sequence, and we know from
> Ramanjan's formula for the partition function that an explicit formula
> is complex and difficult to obtain, and I don't think this has been done
> for A000123, given that the sequence says that even the asymptotics are
> unknown. Nor do I believe that A000123 has a more efficient recurrence
> (e.g, requiring less than O(n) evaluations of a). I do, of course, brook
> correction on these points.
>
> So we are stuck with making the best of [1], ergo stuck using O(n) time
> to compute a(n), which sadly limits how far Paul can extend his sequence
> in his lifetime. However, as I shall explain, a straightforward
> implementation imposes O(n) space as well, which is much more crippling,
> since his available computer memory will exhaust far before the computer
> itself.
>
> If we implement [1] using a straightforward recursion, we eventually
> have to put a(0) through a(n-1) on the stack to compute a(n), this will
> consume O(n) memory (actually O(log n), since each stored a(k) takes up
> at least O(log k) space). Mathematician-clever tricks can be used to
> reduce space by a constant factor, but it takes programmer-clever tricks
> to smack down that O(n).
>
> I will describe a program as soon as I have the spare time. It will
> allow you to compute a(0), a(1), a(2), ... in order, using O(log n)
> additions to compute each successive a(n). It will be somewhat slower
> than storing all the a(k) values, but it will store only O(log n) values
> of a(k), so that you will be able to run it for as long as you like
> (live) without fear of memory overflow.

I don't know if there's a faster recurrence involving only a(k), but
by introducing a related function of 2 variables we can compute a(n)
in O(log(n)^3) operations.  (I'm curious to see the "programmer-clever"
tricks for this.)

For n>=0, let f(n) be the number of partitions of n into powers of 2, so
that a(n) = f(2n) = f(2n+1).  For 0<=k<=e, let g(e,k) be the number of
partitions of 2^e with parts in the set {1,2,4,...,2^k}.

Suppose that  n = 2^e m,  where m is odd.  Consider a typical binary
partition of n:

    n = 2^a(0) + 2^a(1) + ... + 2^a(r)                                  (0)

where  0 <= a(0) <= a(1) <= ... <= a(r).  I claim that one of the partial
sums

    s(i) = 2^a(0) + ... + 2^a(i)                                        (1)

(0 <= i <= r) is equal to 2^e.  If not, then we have

    s(i-1) < 2^e < s(i) = s(i-1) + 2^a(i)                               (2)

for some i.  But then

    n = s(r) = s(i-1) + 2^a(i) + ... + 2^a(r) == s(i-1) (mod 2^a(i))    (3)

If  a(i) > e  then

    s(i-1) == n == 2^e (mod 2^(e+1))                                    (4)

which contradicts  0 <= s(i-1) < 2^e.  So  a(i) <= e  and

    s(i-1) == n == 0 (mod 2^a(i))                                       (5)

Hence  2^e  is a multiple of 2^a(i), but it is strictly between a(i-1) and
a(i), which are two consecutive multiples of  2^a(i).  That's impossible,
so assumption (2) is false.

So every binary partition of n can be split into two parts:

    2^e = 2^a(0) + ... + 2^a(i)                                         (6)

and

    n - 2^e = 2^a(i+1) + ... + 2^a(r)                                   (7)

Given the value of a(i), say a(i)=k, there are  g(e,k)-g(e,k-1)
possibilities for the partition in (6).  (Define g(e,-1)=0 to make
this work for k=0.)  Dividing (7) by 2^k, we see that there are
f((n - 2^e)/2^k) = f(2^(e-k) (m-1))  possibilities for the partition in
(7).  It follows that

    f(n)  =      SUM     (g(e,k) - g(e,k-1)) f(2^(e-k) (m-1))           (8)
             0 <= k <= e

Now we need a recurrence for g(e,k).  An argument similar to the one above
shows that a partition of 2^e, if it has more than one part, can be split
into two partitions of 2^(e-1); this leads to this recurrence:

    g(e,k)  =      SUM     (g(e-1,r) - g(e-1,r-1)) g(e-1-r,k-r)         (9)
               0 <= r <= k

for  0 <= k < e,  and

    g(e,e)  =  1 + g(e,e-1)                                             (10)

Here's a short table of values of g(e,k), computed using (9) and (10):

 \ k 0    1    2       3       4       5       6       7       8
e \-------------------------------------------------------------
0 |  1       
1 |  1    2    
2 |  1    3     4       
3 |  1    5     9     10      
4 |  1    9    25     35      36      
5 |  1   17    81    165     201     202     
6 |  1   33   289    969    1625    1827    1828    
7 |  1   65  1089   6545   17361   25509   27337   27338   
8 |  1  129  4225  47905  222241  497097  664665  692003  692004  

Note that the values along the diagonal are f(1), f(2), f(2^2), f(2^3), ...
So computing f of a power of 2 is easy.  Other values of f can be
obtained using equation (8) above; typically computing f(n) requires
O(log(n)^2) previous values of f, in addition to O(log(n)^2) values of
g, and takes O(log(n)^3) operations.

I missed the beginning of this discussion, so I don't know the
significance of  A000123( (2^(n+1) + (-1)^n - 3)/6 ),  but here are some
values of it:

0   1
1   2
2   4
3   14
4   60
5   450
6   4964
7   95982
8   3037948
9   170005730
10  16522010532
11  2882717916878
12  902450057292988
13  514768747418386946
14  537142988843543963620
15  1033976171696917695108270
16  3688322935382700002945333884
17  24514290054855626308893309022498
18  304801526292655801235227790576374820
19  7118057806591130565341301223201553053838
20  313259388933361321062892077969687559682278460
21  26062000616057795214066097831489188710729238725186
22  4110314988488084452256619909138315265238808174186210404
23  1232011347867422753097623155438444291235913527367600775244398
24  703443704442715976354754937644920300954525944513753257422978632188
25  766728692007699092343031760182847052574421909903729669942757487137772898


Here's Mathematica code for all of this:


e2[n_]:=
(* Maximum exponent e such that 2^e divides n.  (n>=1) *)
If[OddQ[n], 0, 1+e2[n/2]];

f[0]=1;
f[n_]:=f[n]=
(* Number of binary partitions of n.  (n>=0) *)
Module[{e,m},
  e=e2[n];
  m=n/2^e;
  Sum[(g[e,k]-g[e,k-1]) f[2^(e-k) (m-1)], {k,0,e}]
];
  
g[e_,k_]:=g[e,k]=
(* Number of partitions of 2^e with parts in {1,2,4,...,2^k} (-1 <= k <= e) *)
Module[{},      
  Which[
    k<0,        0,
    k==0,       1,
    k==e,       g[e,e-1]+1,
    True,       Sum[(g[e-1,r]-g[e-1,r-1]) g[e-1-r,k-r], {r,0,k}] ] ];

a[n_]:=
(* Sequence A000123, number of binary partitions of 2n.  (n>=0) *)
f[2n];

h[n_]:=a[(2^(n+2)-(-1)^n-3)/6];


Dean Hickerson
dean at math.ucdavis.edu





More information about the SeqFan mailing list