Disarium numbers

From Rosetta Code
Revision as of 21:43, 15 February 2022 by Jjuanhdez (talk | contribs) (Disarium numbers en BASIC256 y PureBasic)
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 

BASIC

BASIC256

<lang freebasic>function isDisarium(n) digitos = length(string(n)) suma = 0 x = n while x <> 0 suma += (x % 10) ^ digitos digitos -= 1 x = x \ 10 end while if suma = n then return True else return False end function

limite = 19 cont = 0 : n = 0 print "The first"; limite; " Disarium numbers are:" while cont < limite if isDisarium(n) then print n; " "; cont += 1 endif n += 1 end while end</lang>

Output:
Igual que la entrada de FreeBASIC.

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.

PureBasic

<lang PureBasic>Procedure isDisarium(n.i)

 digitos.i = Len(Str(n))
 suma.i = 0
 x.i = n
 While x <> 0
   r.i = (x % 10)
   suma + Pow(r, digitos)
   digitos - 1
   x / 10
 Wend
 If suma = n 
   ProcedureReturn #True 
 Else 
   ProcedureReturn #False
 EndIf

EndProcedure

OpenConsole() limite.i = 19 cont.i = 0 n.i = 0 PrintN("The first" + Str(limite) + " Disarium numbers are:") While cont < limite

 If isDisarium(n)
   Print(Str(n) + #TAB$)
   cont + 1
 EndIf
 n + 1

Wend Input() CloseConsole()</lang>

Output:
Igual que la entrada de FreeBASIC.

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

Version 1 (Brute force)

This version finds the first 19 Disarium numbers in 3.35 seconds though, clearly, finding the 20th is out of the question with this approach.

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. <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]


Version 2 (Much faster)

This is a translation of the C code referred to in the Phix entry and finds the first 19 Disarium numbers in 0.012 seconds.

Efficient though this method is, unfortunately finding the 20th is still out of reasonable reach for Wren. If we let this run until 15 digit numbers have been examined (the most that 53 bit integer math can accurately manage), then the time taken rises to 19 seconds - roughly 3 times slower than Phix.

However, we need 64 bit integer arithmetic to get up to 20 digits and this requires the use of Wren-long which (as it's written entirely in Wren, not C) needs about 7 times longer (2 minutes 16 seconds) to even reach 15 digits. Using BigInt or GMP would be even slower.

So, if the Phix example requires 48 minutes to find the 20th number, it would probably take Wren the best part of a day to do the same which is far longer than I have patience for.

Wren

<lang ecmascript>var DMAX = 7 // maxmimum digits var LIMIT = 19 // maximum number of Disariums to find

// Pre-calculated exponential and power serials var EXP = List.filled(1 + DMAX, null) var POW = List.filled(1 + DMAX, null) EXP[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1] EXP[1] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] POW[0] = List.filled(11, 0) POW[1] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9] for (i in 2..DMAX) {

   EXP[i] = List.filled(11, 0)
   POW[i] = List.filled(11, 0)

} for (i in 1...DMAX) {

   for (j in 0..9) {
       EXP[i+1][j] = EXP[i][j] * 10
       POW[i+1][j] = POW[i][j] * j
   }
   EXP[i+1][10] = EXP[i][10] * 10
   POW[i+1][10] = POW[i][10] + POW[i+1][9]

}

// Digits of candidate and values of known low bits var DIGITS = List.filled(1 + DMAX, 0) // Digits form var Exp = List.filled(1 + DMAX, 0) // Number form var Pow = List.filled(1 + DMAX, 0) // Powers form

var exp var pow var min var max var start = 1 var final = DMAX var count = 0 for (digit in start..final) {

   System.print("# of digits: %(digit)")
   var level = 1
   DIGITS[0] = 0
   while (true) {
       // Check limits derived from already known low bit values
       // to find the most possible candidates
       while (0 < level && level < digit) {
           // Reset path to try next if checking in level is done
           if (DIGITS[level] > 9) {
               DIGITS[level] = 0
               level = level - 1
               DIGITS[level] = DIGITS[level] + 1
               continue
           }
           // Update known low bit values
           Exp[level] = Exp[level - 1] + EXP[level][DIGITS[level]]
           Pow[level] = Pow[level - 1] + POW[digit + 1 - level][DIGITS[level]]
           // Max possible value
           pow = Pow[level] + POW[digit - level][10]
           if (pow < EXP[digit][1]) {  // Try next since upper limit is invalidly low
               DIGITS[level] = DIGITS[level] + 1
               continue
           }
           max = pow % EXP[level][10]
           pow = pow - max
           if (max < Exp[level]) pow = pow - EXP[level][10]
           max = pow + Exp[level]
           if (max < EXP[digit][1]) {  // Try next since upper limit is invalidly low
               DIGITS[level] = DIGITS[level] + 1
               continue
           }
           // Min possible value
           exp = Exp[level] + EXP[digit][1]
           pow = Pow[level] + 1
           if (exp > max || max < pow) { // Try next since upper limit is invalidly low
               DIGITS[level] = DIGITS[level] + 1
               continue
           }
           if (pow > exp ) {
               min = pow % EXP[level][10]
               pow = pow - min 
               if (min > Exp[level]) {
                   pow = pow + EXP[level][10]
               }
               min = pow + Exp[level]
           } else {
               min = exp
           }
           // Check limits existence
           if (max < min) {
               DIGITS[level] = DIGITS[level] + 1  // Try next number since current limits invalid
           } else {
               level= level + 1  // Go for further level checking since limits available
           }
       }
       // All checking is done, escape from the main check loop
       if (level < 1) break
       // Finally check last bit of the most possible candidates
       // Update known low bit values
       Exp[level] = Exp[level - 1] + EXP[level][DIGITS[level]]
       Pow[level] = Pow[level - 1] + POW[digit + 1 - level][DIGITS[level]]
       // Loop to check all last bits of candidates
       while (DIGITS[level] < 10) {
           // Print out new Disarium number
           if (Exp[level] == Pow[level]) {
               var s = ""
               for (i in DMAX...0) s = s + DIGITS[i].toString
               System.print(Num.fromString(s))
               count = count + 1
               if (count == LIMIT) {
                   System.print("\nFound the first %(LIMIT) Disarium numbers.")
                   return
               }
           }
           // Go to followed last bit candidate
           DIGITS[level] = DIGITS[level] + 1
           Exp[level] = Exp[level] + EXP[level][1]
           Pow[level] = Pow[level] + 1
       }
       // Reset to try next path
       DIGITS[level] = 0
       level = level - 1
       DIGITS[level] = DIGITS[level] + 1
   }
   System.print()

}</lang>

Output:
# of digits: 1
0
1
2
3
4
5
6
7
8
9

# of digits: 2
89

# of digits: 3
135
175
518
598

# of digits: 4
1306
1676
2427

# of digits: 5

# of digits: 6

# of digits: 7
2646798

Found the first 19 Disarium numbers.

real	0m0.012s
user	0m0.008s
sys	0m0.004s