Ponder This

alec's picture

Joe Riel and Thomas Richard posted in their blogs solutions to the latest IBM Ponder This challenge and some related questions. From the name of the attached files, one can deduct that Thomas Richard is user No. 50 and Joe Riel is user No. 84. Being curious about my user number, I also decided to attach a worksheet.

Comments

A bit faster

Nice improvement, Alec, thanks. Slightly clearer, I think, and a bit faster is to replace trunc( (...)/i) with iquo( (...), i). Faster yet (still not by much) is to replace the three calls to irem() with a single call that assigns a local variable. One implementation is

Sols3 := proc(b)
local A,i,j,k,arem,rem;
    arem := proc() rem := irem(b*j,i) end;
    A := table();
    A[1] := [$1..b-1];
    for i from 2 do
        A[i] := [seq](seq(b*j+k*i-rem
                          ,k = `if`(arem()=0,0,1) .. iquo(b-1+rem,i))
                      ,j = A[i-1]);
        if A[i] = [] then break fi
    od;
    i-1 = A[i-1]
end proc:
alec's picture

Even faster

Thank you, Joe. Both things are good. The next improvement can be achieved by separating the loop in 2 parts,

a:=proc(b)
local A,i,j,k,arem,brem,rem;
arem:=proc() rem:= irem(b*j,i); 
     seq(b*j+k*i-rem,k = `if`(rem=0,0,1) .. iquo(b-1+rem,i)) end;
brem:=proc() rem:=irem(b*j,i);
     if rem=0 then b*j elif b-1+rem<i then NULL else b*j+i-rem fi end;
A[1] := [$1..b-1];
for i from 2 to b-1 do
     A[i] := [seq](arem(), j = A[i-1]) od;
for i from b do 
     A[i] := [seq](brem(), j = A[i-1]);
     if A[i] = [] then break fi od;
i-1 = A[i-1]
end:
alec's picture

Improved version for Dumb Kopf

Correspondingly, here is the improved version of a1,

a1:=proc(b)
local A,i,j,k,arem,rem,dig;
arem:=proc() rem:=irem(b*j,i); dig:=convert(j,base,b); 
     seq(`if`(member(k*i-rem,dig),NULL,b*j+k*i-rem),
          k = 1 .. iquo(b-1+rem,i)) end;
A[1] := [$1..b-1];
for i from 2 do
     A[i] := [seq](arem(), j = A[i-1]);
     if A[i] = [] then break fi od;
i-1 = A[i-1]
end:

For decimal system, it works very fast,

tt:=time(): a1(10); time()-tt;

                           9 = [381654729]

                                0.016

In addition to the values in the worksheet, I calculated

a1(21);
                   18 = [509504810627259318940020]

a1(22);

                   19 = [5213608783930183587520297]

a1(23);

  21 = [3017921136920002608782486643, 3202513921881602294966195322,

        10912277070078955588719411381, 11506928512609494037599575154,

        19136629049078922486640055262, 19563154864313291881366262910,

        20927442077211608842020972981, 22095893110934309880723905997,

        24479055281091281744429147151, 26034588683899462487295851544,

        27944901128718982841182304058, 28236074834439966722409861288,

        30327538677373615144106864301, 30327538677373615144106864322,

        33286709815522787435710859625, 33637644479370238638607793874,

        39311846134353288073052119134]
alec's picture

Using Compiler for Dumb Kopf

Also, the compiled procedure for Dumb Kopf is much faster. Again, it is just the first try - I have no doubt that it can be further improved,

a5 := proc(b::integer[4], A::Array(datatype = integer[4]))::
integer[4];
local c::integer[4], i::integer[4], j::integer[4],
k::integer[4], m::integer[4], K::integer[4], s::integer[4],
r::integer[4];
    A[1] := 1;
    i := 2;
    K := 1;
    s := 1;
    while 0 < i do
        if s = 1 then
            r := irem(A[1], i);
            for j from 2 to i - 1 do r := irem(r*b + A[j], i)
            end do;
            r := irem(r*b, i);
            c := -r;
            s := 0;
            for k to iquo(b - 1 + r, i) do
                c := c + i;
                for m to i - 1 do
                    if c = A[m] then break end if
                end do;
                if m = i then
                    A[i] := c;
                    K := max(K, i);
                    i := i + 1;
                    s := 1;
                    break
                end if
            end do;
            if s = 0 then i := i - 1 end if
        else
            if A[i] + i < b then
                c := A[i];
                for k to iquo(b - 1 - A[i], i) do
                    c := c + i;
                    for m to i - 1 do
                        if c = A[m] then break end if
                    end do;
                    if m = i then
                        A[i] := c; i := i + 1; s := 1; break
                    end if
                end do;
                if s = 0 then i := i - 1 end if
            else i := i - 1
            end if
        end if
    end do;
    K
end proc:

a6:=Compiler:-Compile(a5):
A:=Array(1..100,datatype=integer[4]):

It should be called as a6(b,A) where b is the base and A is a predefined Array with datatype=integer[4] and length greater than or equal to b. It returns the maximal number of digits for the Dumb Kopf problem. In particular, the returned values for b from 24 to 32 are 19, 23, 22, 24, 23, 27, 24, 29, 27.

alec's picture

Using Compiler

Now, compiled procedures should run much faster. Here is the first try,

a2 := proc(b::integer[4], A::Array(datatype = integer[4]))::
integer[4];
local i::integer[4], j::integer[4], K::integer[4],
s::integer[4], r::integer[4];
    A[1] := 1;
    i := 2;
    K := 1;
    s := 1;
    while 0 < i do
        if s = 1 then
            r := irem(A[1], i);
            for j from 2 to i - 1 do r := irem(r*b + A[j], i)
            end do;
            r := irem(r*b, i);
            if r = 0 then
                A[i] := 0; K := max(K, i); i := i + 1
            elif b - 1 + r < i then i := i - 1; s := 0
            else A[i] := i - r; K := max(K, i); i := i + 1
            end if
        else
            if A[i] + i < b then
                A[i] := A[i] + i; i := i + 1; s := 1
            else i := i - 1
            end if
        end if
    end do;
    K
end proc:

a3:=Compiler:-Compile(a2):
A:=Array(1..100,datatype=integer[4]):

While a2 is slower than a, the compiled procedure a3 is much faster. For example,

tt:=time(): a3(10,A); time()-tt;
                                  25
                                0.016

It should be called as a3(b,A) where b is the base and A is a predefined Array with datatype=integer[4] and length greater than the expected maximal number of digits. It returns the maximal number of digits. In particular, for b from 17 to 21 the maximal number of digits has appeared to be 45, 48, 48, 52, 53.

alec's picture

Using assembler

To do further calculations, I produced a dll with code written in assembler. In Maple, it can be called as pt(b,A); for example, pt(10,A) for base 10 after defining

pt:=define_external( 
       'pt', 
       'b'::integer[1], 
       'A'::ARRAY(1..100,datatype=integer[1]), 
       'RETURN'::integer[4], 
       'LIB'="C:/MyProjects/Assembly/Ponder/Ponder.dll" 
);
A:=Array(1..100,datatype=integer[1]);

with changing the value of LIB to the location of Ponder.dll.

It appeared to be about 3 times faster than the compiled procedure a3. Here are the results of calculations for b from 2 to 23,

2, 6, 7, 10, 11, 18, 17, 22, 25, 26, 28, 35, 39, 38, 39, 45, 48, 48, 52, 53, 56, 58.

alec's picture

Compiled procedure with printing

Now, when we calculated the maximal number of digits for different bases, the numbers with these number of digits also can be calculated. Here is the correspondingly changed procedure,

a7 := proc(b::integer[4], A::Array(datatype = integer[4]),
    N::integer[4])::integer[4];
local i::integer[4], j::integer[4], s::integer[4], r::integer[4];
    A[1] := 1;
    i := 2;
    s := 1;
    while 0 < i do
        if i = N + 1 then printf("%d\n", A)
        end if;
        if s = 1 then
            r := irem(A[1], i);
            for j from 2 to i - 1 do r := irem(r*b + A[j], i)
            end do;
            r := irem(r*b, i);
            if r = 0 then A[i] := 0; i := i + 1
            elif b - 1 + r < i then i := i - 1; s := 0
            else A[i] := i - r; i := i + 1
            end if
        else
            if A[i] + i < b then
                A[i] := A[i] + i; i := i + 1; s := 1
            else i := i - 1
            end if
        end if
    end do
end proc:

a8:=Compiler:-Compile(a7):
A:=Array(1..100,datatype=integer[4]):

This procedure called as a8(b,A,N) prints all N-digit numbers in base b satisfying the problem conditions. Here are the new numbers produced by it, written as sequences of their digits in base b.

a8(17,A[1..45],45):
9 7 10 6 3 9 1 11 10 14 13 11 3 5 2 14 0 10 15 11 8
8 10 2 4 0 2 2 6 4 12 2 8 0 10 10 11 5 3 1 13 3 2 6 13 

a8(18,A[1..48],48):
10 14 6 16 10 0 7 10 9 14 10 0 4 2 3 2 2 0 16 16 15 6 16 0 14 4 9 2 16
12 6 4 0 0 16 0 13 4 0 8 3 0 8 0 0 6 14 12 

a8(19,A[1..48],48):
4 16 10 2 4 0 12 4 2 10 11 15 10 18 17 13 6 8 0 10 2 16 10 16 10 18 17
13 18 14 17 17 2 2 10 6 12 0 9 5 9 7 7 9 5 7 11 9 
15 7 11 11 17 11 12 6 0 0 10 14 4 12 2 14 1 15 0 2 1 9 16 8 10 18 8 0
7 17 14 0 13 1 4 16 16 0 5 5 2 2 0 12 12 12 6 18 

a8(20,A[1..52],52):
17 16 8 16 5 4 6 8 2 0 12 0 0 6 0 16 1 12 4 0 2 0 6 0 0 18 12 0 10 10
16 0 11 6 0 12 18 12 0 0 16 10 16 8 10 18 14 8 15 0 12 12 

a8(21,A[1..53],53):
12 16 12 0 0 12 14 6 9 9 18 0 11 7 9 5 0 18 10 12 0 10 12 18 5 13 6 0
8 18 18 0 0 18 14 12 14 0 0 14 4 0 20 8 3 19 9 9 7 11 3 11 0 
17 1 0 2 15 3 7 7 6 2 3 9 6 14 18 2 18 18 14 18 0 6 17 9 8 6 18 0 18
18 13 19 0 6 7 15 0 12 3 5 14 0 20 6 0 14 8 18 14 16 15 11 18 
alec's picture

Base 16

Meanwhile, I calculated a(16):

39 = [18872900738885736149574055538327802527212537551, 60753927368683934227793588395570842550542338031, 89515749136034833729775437005460258167590093634]

Converted to hex, the numbers look like

34E4A468166CD8604EC0F8106AB4326098286CF
AA44CE207C78FC30003C3CC0D8382E2078D07EF
FAE06678C2E884607EB8B4E0B0A0F0603420342

alec's picture

Something in common

Looking at my user number, 135, Joe Riel's 84, and Thomas Richard's 50, I found out that we have something in common.

gcd(50,84)=2
gcd(50,135)=5
gcd(84,135)=3

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
}