Disarium numbers

From Rosetta Code
Revision as of 11:48, 15 February 2022 by Petelomax (talk | contribs) (→‎{{header|Phix}}: added stretch)
Disarium numbers is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

A Disarium number is an integer where the sum of each digit raised to the power of its position in the number, is equal to the number.


E.G.

135 is a Disarium number:

11 + 32 + 53 == 1 + 9 + 125 == 135

There are a finite number of Disarium numbers.


Task
  • Find and display the first 18 Disarium numbers.


Stretch
  • Find and display all 20 Disarium numbers.


See also



Factor

Works with: Factor version 0.99 2021-06-02

<lang factor>USING: io kernel lists lists.lazy math.ranges math.text.utils math.vectors prettyprint sequences ;

disarium? ( n -- ? )
   dup 1 digit-groups dup length 1 [a,b] v^ sum = ;
disarium ( -- list ) 0 lfrom [ disarium? ] lfilter ;

19 disarium ltake [ pprint bl ] leach nl</lang>

Output:
0 1 2 3 4 5 6 7 8 9 89 135 175 518 598 1306 1676 2427 2646798 

FreeBASIC

<lang freebasic>#define limite 19

Function isDisarium(n As Integer) As Boolean

   Dim As Integer digitos = Len(Str(n))
   Dim As Integer suma = 0, x = n
   While x <> 0
       suma += (x Mod 10) ^ digitos
       digitos -= 1
       x \= 10
   Wend
   Return Iif(suma = n, True, False)

End Function

Dim As Integer cont = 0, n = 0, i Print "The first"; limite; " Disarium numbers are:" Do While cont < limite

   If isDisarium(n) Then
       Print n; " ";
       cont += 1
   End If
   n += 1

Loop Sleep</lang>

Output:
Igual que la entrada de Python.

Julia

<lang julia>isdisarium(n) = sum(last(p)^first(p) for p in enumerate(reverse(digits(n)))) == n

function disariums(numberwanted)

   n, ret = 0, Int[]
   while length(ret) < numberwanted
       isdisarium(n) && push!(ret, n)
       n += 1
   end
   return ret

end

println(disariums(19)) @time disariums(19)

</lang>

Output:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 89, 135, 175, 518, 598, 1306, 1676, 2427, 2646798]
  0.555962 seconds (5.29 M allocations: 562.335 MiB, 10.79% gc time)

Perl

<lang perl>use strict; use warnings;

my ($n,@D) = (0, 0); while (++$n) {

   my($m,$sum);
   map { $sum += $_ ** ++$m } split , $n;
   push @D, $n if $n == $sum;
   last if 19 == @D;

} print "@D\n";</lang>

Output:
0 1 2 3 4 5 6 7 8 9 89 135 175 518 598 1306 1676 2427 2646798

Phix

with javascript_semantics
constant limit = 19
integer count = 0, n = 0
printf(1,"The first 19 Disarium numbers are:\n")
while count<limit do
    atom dsum = 0
    string digits = sprintf("%d",n)
    for i=1 to length(digits) do
         dsum += power(digits[i]-'0',i)
    end for
    if dsum=n then
        printf(1," %d",n)
        count += 1
    end if
    n += 1
end while
Output:
The first 19 Disarium numbers are:
 0 1 2 3 4 5 6 7 8 9 89 135 175 518 598 1306 1676 2427 2646798

stretch

with javascript_semantics
-- translation of https://github.com/rgxgr/Disarium-Numbers/blob/master/Disarium.c
constant DMAX = iff(machine_bits()=64?20:7)

// Pre-calculated exponential & power serials
sequence exps = repeat(repeat(0,11),1+DMAX),
         pows = repeat(repeat(0,11),1+DMAX)
exps[1..2] = {{0,0,0,0,0,0,0,0,0,0,1},{0,1,2,3,4,5,6,7,8,9,10}}
pows[1..2] = {{0,0,0,0,0,0,0,0,0,0,0},{0,1,2,3,4,5,6,7,8,9, 9}}
for i=2 to DMAX do
    for j=1 to 10 do
        exps[i+1][j] = exps[i][j]*10
        pows[i+1][j] = pows[i][j]*(j-1)
    end for
    exps[i+1][11] = exps[i][11]*10
    pows[i+1][11] = pows[i][11] + pows[i+1][10]
end for

// Digits of candidate and values of known low bits
sequence digits = repeat(0,1+DMAX), // Digits form
         expl = repeat(0,1+DMAX),   // Number form
         powl = repeat(0,1+DMAX)    // Powers form

printf(1,"") -- (exclude console setup from timings [if pw.exe])
atom expn, powr, minn, maxx, t0 = time(), t1 = t0+1, count = 0
for digit=2 to DMAX+1 do
    printf(1,"Searching %d digits (started at %s):\n", {digit-1,elapsed(time()-t0)});
    integer level = 2
    digits[1] = 0
    while true do
        // Check limits derived from already known low bit values
        // to find the most possible candidates
        while 1<level and level<digit do
            // Reset path to try next if checking in level is done
            integer dl = digits[level]+1
            if dl>10 then
                digits[level] = 0;
                level -= 1
                digits[level] += 1
            else
                // Update known low bit values
                expl[level] = expl[level-1] + exps[level][dl]
                powl[level] = powl[level-1] + pows[digit-level+2][dl]

                // Max possible value
                powr = powl[level] + pows[digit-level+1][11]

                atom ed2 = exps[digit][2]
                if powr<ed2 then  // Try next since upper limit is invalidly low
                    digits[level] += 1
                else
                    atom el11 = exps[level][11],
                         el = expl[level]
                    maxx = remainder(powr,el11)
                    powr -= maxx
                    if maxx<el then
                        powr -= el11
                    end if
                    maxx = powr + el
                    if maxx<ed2 then  // Try next since upper limit is invalidly low
                        digits[level] += 1
                    else
                        // Min possible value
                        expn = el + ed2
                        powr = powl[level] + 1

                        if expn>maxx or maxx<powr then // Try next since upper limit is invalidly low
                            digits[level] += 1
                        else
                            if powr>expn then
                                minn = remainder(powr,el11)
                                powr -= minn
                                if minn>el then
                                    powr += el11
                                end if
                                minn = powr + el
                            else
                                minn = expn
                            end if

                            // Check limits existence
                            if maxx<minn then
                                digits[level] +=1   // Try next number since current limits invalid
                            else
                                level +=1   // Go for further level checking since limits available
                            end if
                        end if
                    end if
                end if
            end if
            if time()>t1 and platform()!=JS then
                progress("working:%v... (%s)",{digits,elapsed(time()-t0)})
                t1 = time()+1
            end if
        end while
      
        // All checking is done, escape from the main check loop
        if level<2 then exit end if

        // Final check last bit of the most possible candidates
        // Update known low bit values
        integer dlx = digits[level]+1
        expl[level] = expl[level-1] + exps[level][dlx];
        powl[level] = powl[level-1] + pows[digit+1-level][dlx];

        // Loop to check all last bit of candidates
        while digits[level]<10 do
            // Print out new disarium number
            if expl[level] == powl[level] then
                if platform()!=JS then progress("") end if
                integer ld = max(trim_tail(digits,0,true),2)
                printf(1,"%s\n",{reverse(join(apply(digits[2..ld],sprint),""))})
                count += 1
            end if

            // Go to followed last bit candidate
            digits[level] += 1
            expl[level] += exps[level][2]
            powl[level] += 1
        end while

        // Reset to try next path
        digits[level] = 0;
        level -= 1
        digits[level] += 1
    end while
    if platform()!=JS then progress("") end if
end for
printf(1,"%d disarium numbers found (%s)\n",{count,elapsed(time()-t0)})
Output:
Searching 1 digits (started at 0s):
0
1
2
3
4
5
6
7
8
9
Searching 2 digits (started at 0s):
89
Searching 3 digits (started at 0s):
135
175
518
598
Searching 4 digits (started at 0s):
1306
1676
2427
Searching 5 digits (started at 0.0s):
Searching 6 digits (started at 0.0s):
Searching 7 digits (started at 0.0s):
2646798
Searching 8 digits (started at 0.0s):
Searching 9 digits (started at 0.0s):
Searching 10 digits (started at 0.0s):
Searching 11 digits (started at 0.1s):
Searching 12 digits (started at 0.1s):
Searching 13 digits (started at 0.3s):
Searching 14 digits (started at 0.8s):
Searching 15 digits (started at 2.5s):
Searching 16 digits (started at 6.9s):
Searching 17 digits (started at 23.2s):
Searching 18 digits (started at 1 minute and 8s):
Searching 19 digits (started at 3 minutes and 35s):
Searching 20 digits (started at 10 minutes and 8s):
12157692622039623539
20 disarium numbers found (2 hours and 7s)

Takes about 48min to find the 20 digit number, then trundles away for over another hour. I think that technically it should also scan for 21 and 22 digit numbers to be absolutely sure there aren't any, but that certainly exceeds my patience.

Python

<lang python>#!/usr/bin/python

def isDisarium(n):

   digitos = len(str(n))
   suma = 0
   x = n
   while x != 0:
       suma += (x % 10) ** digitos
       digitos -= 1
       x //= 10
   if suma == n:
       return True
   else:
       return False

if __name__ == '__main__':

   limite = 19
   cont = 0
   n = 0
   print("The first",limite,"Disarium numbers are:")
   while cont < limite:
       if isDisarium(n):
           print(n, end = " ")
           cont += 1
       n += 1</lang>
Output:
The first 19 Disarium numbers are:
 0 1 2 3 4 5 6 7 8 9 89 135 175 518 598 1306 1676 2427 2646798


Raku

Not an efficient algorithm. First 18 in less than 1/4 second. 19th in around 45 seconds. Pretty much unusable for the 20th. <lang perl6>my $disarium = (^∞).hyper.map: { $_ if $_ == sum .polymod(10 xx *).reverse Z** 1..* };

put $disarium[^18]; put $disarium[18];</lang>

Output:
0 1 2 3 4 5 6 7 8 9 89 135 175 518 598 1306 1676 2427
2646798

Wren

Library: Wren-math

Well, for once, we can brute force the first 19 Disarium numbers without resorting to BigInt or GMP. Surprisingly, much quicker than Raku at 3.35 seconds.

As a possible optimization, I tried caching all possible digit powers but there was no perceptible difference in running time for numbers up to 7 digits long.

Taking a rain check on the 20th number which is 20 digits long (too big for Wren's 53 bit integers) to see whether more efficient methods emerge. <lang ecmascript>import "./math" for Int

var limit = 19 var count = 0 var disarium = [] var n = 0 while (count < limit) {

   var sum = 0
   var digits = Int.digits(n)
   for (i in 0...digits.count) sum = sum + digits[i].pow(i+1)
   if (sum == n) {
       disarium.add(n)
       count = count + 1
   }
   n = n + 1

} System.print("The first 19 Disarium numbers are:") System.print(disarium)</lang>

Output:
The first 19 Disarium numbers are:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 89, 135, 175, 518, 598, 1306, 1676, 2427, 2646798]