Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
29372 Discussions

What language would they have used

JohnNichols
Valued Contributor III
9,133 Views

I stumbled across this little gem, it is correct - checked it with excel -- did they likely do it in Fortran and what algorithm do you think they used?

https://www.ams.org/journals/mcom/1967-21-099/S0025-5718-1967-0222008-0/S0025-5718-1967-0222008-0.pdf

This paper sets out some of the findings as well from 1967,  this is a big search and overflow would be a real issue

The two are from the aerospace co - why would they be interested in this pure math --

main-qimg-6dcd42be2321a6a8c9d32e33e44b13a5.jpg

0 Kudos
53 Replies
David-F8
Novice
401 Views
    subroutine FindCounterExamples(uLimit)
        integer(INT64), intent(in) :: uLimit
    
        logical        :: found
        integer(INT64) :: maxRoot, m
        integer(INT64) :: iterations
        integer(INT64) :: a, b, c, d, n
        integer(INT64) :: sum
        real(REAL64)   :: seconds
        integer(INT64), rank(1), allocatable :: powers
        integer(INT64), rank(1), allocatable :: roots
        type(TTimer)   :: timer
        
        call timer % Start()
        
        call DisplayHeader(uLimit)
        
        maxRoot = LargestRoot(uLimit)
        m = HashTableSize(uLimit)
        call BuildFifthPowers(maxRoot, powers)
        call BuildFifthRoots(maxRoot, m, powers, roots)
        
        iterations = 0
        
        do a = 1, uLimit
            do b = a, uLimit
                do c = b, uLimit
                    do d = c, uLimit
                        iterations = iterations + 1
                        sum = powers(a) + powers(b) + powers(c) + powers(d)
                        call TestFifthPower(sum, powers, roots, n, found)
                        if (found) then
                            call DisplayResult(a, b, c, d, n)
                        end if
                    end do
                end do
            end do
        end do
        
        call timer % Stop()
        seconds = timer % Elapsed()
        
        call DisplaySummary(iterations, seconds)

    end subroutine FindCounterExamples

This is the main loop.  Hopefully the gist is fairly clear.  I'll endeavour to attach the Visual Studio solution so you can see all the details, and maybe leave in my comments at the top of the file EulersConjecture.f90 that records a summary of the changes I made.

I call a couple of subroutines before the loops to set up some data that is used in the main loop.  The 5th powers are all pre-computed and stored in powers, e.g powers(i) = i**5.   Similarly, roots is a hash table that is the inverse of powers, e.g. given an integer 5th power i**5, use roots to recover the value i.  This is used in the subroutine TestFifthPowers to determine if the sum of 5th powers is itself a 5th power.  The function HashTableSize is used to determine a smallish hash table size that also ensures there are no hash collisions.

Finally, the subroutine TestFifthPowers also makes use of the number theorist's trade secret posted above by (at that time) Mr Mecej4, to avoid testing numbers that cannot be 5th powers.

I tried a couple of additional things that are not present in the attached code.

  1. A running total of the 5th power partial sums in the outer loops.  This saved an additional 0.2s
  2. Inlining the functionality from the subroutine TestFifthPower.  This saved an additional 0.4s.

But it made the source code too ugly, so I reverted those changes.

 

Here are the details for TestFifthPowers...

    subroutine TestFifthPower(n, powers, roots, i, found)
        ! Test if n is a 5th power.
        ! If it is, return the value i such that i**5 = n.
    
        integer(INT64), intent(in) :: n
        integer(INT64), rank(1), intent(in) :: powers
        integer(INT64), rank(1), intent(in) :: roots(0:)
        integer(INT64), intent(out) :: i
        logical, intent(out) :: found
        
        integer(INT64) :: m
        integer(INT32) :: k
        
        ! Early exit. If n does not meet the requirements for a 5th power, no need to test further.
        if (.not. IsPotentialFifthPower(n)) then
            found = .false.
            return
        end if
        
        ! Use the roots lookup table to get a candidate root, then test it.
        m  = Size(roots)
        k = Modulo(n, m)
        i = roots(k)
        found = (powers(i) == n)
        
    end subroutine TestFifthPower

 

 

 

mecej4O
New Contributor I
269 Views

I downloaded your Zip file, extracted the files, then built and ran your program. Nice work, @David-F8 !  A nice feature to add would be to take each output solution, find the GCF (greatest common factor) of the five numbers that are raised to the fifth power, and divide each of them by the GCF, and then output the "primitive solution", as it is called.

0 Kudos
David-F8
Novice
188 Views

Agreed.  But I'm definitely not going to do that.

After a bit of tweaking, I have my run time down to 4.4s.

But, apart from multi-threading, I think I'm done with that particular approach.

Working on another idea now.

0 Kudos
David-F8
Novice
438 Views

Forgot...

I've been running it from the command line.  The first (only) command line argument is the upper limit for the search.  I've been using 600.

PS C:\Users\David\Source\Fortran\EulersConjecture\EulersConjecture> .\x64\Release\EulersConjecture.exe 600

Testing numbers 1...600

   Determine table size (0.000 seconds)

    Building 5th powers (0.000 seconds)

     Building 5th roots (0.000 seconds)

 27^5 +  84^5 + 110^5 + 133^5 = 144^5
 54^5 + 168^5 + 220^5 + 266^5 = 288^5
 81^5 + 252^5 + 330^5 + 399^5 = 432^5
108^5 + 336^5 + 440^5 + 532^5 = 576^5

5454165150 iterations in 5.636 seconds (9.677+08 iter/s)

Done.

 

GVautier
New Contributor III
342 Views

Wouldn't be quicker to reverse the problem? For each 5th power, try to find a combination of 5th power numbers. 

0 Kudos
mecej4O
New Contributor I
276 Views

I don't know what you mean by "reverse the problem". If you know n^5, and you wish to find i,j,k,l,m such that i^5+j^5+k^5+l^5+m^5=n^5, there are two obstacles. 1. How do you know that a solution exists? 2. If no solution exists, is your algorithm capable of detecting that fact, and can it end the calculation safely?

0 Kudos
David-F8
Novice
190 Views

I thought that reversing the problem might be a knapsack-type approach:  Take one of the fifth powers as a starting point (the target sum), and try to find 4 smaller fifth powers that add to the target sum.

This is a hard problem in general, not sure if it would be beneficial for this particular problem.  In the worst case (which is most of time) you'd end up doing an (n choose 4) search through the list of fifth powers and not find a solution... and you need to do this n times, one for each of the target sums.

0 Kudos
GVautier
New Contributor III
271 Views

My idea was for each n^5 test the sum of 5 i^5in 5 nested loops. Each inner loop will be limited to the value of the outer loop and can be stopped as soon as the sum overflow n^5. Barely it seems possible that would limit the number of iterations.

0 Kudos
mecej4O
New Contributor I
253 Views

If you look at @David-F8's post in this thread, you will see nested loops of the type that you suggest. The key is to make sure that the loops cover just the needed combinations, and no more.

0 Kudos
GVautier
New Contributor III
269 Views

Something like that

do n=1,nmax
    do i=1,n-1
        do j=1,i-1
            sum(1)=pow5(i)+pow5(j)
            if (sum(1).ge.pow5(n)) exit
            do k=1,j-1
                sum(2)=sum(1)+pow5(k)
                if (sum(2).ge.pow5(n)) exit
                do l=1,k-1
                    sum(3)=sum(2)+pow5(l)
                    if (sum(3).ge.pow5(n)) exit
                    do m=1,l-1
                        sum(4)=sum(3)+pow5(m)
                        if (sum(4).eq.pow5(n)) then
                            write 'OK'
                            exit
                        endif
                        if (sum(4).gt.pow5(n)) exit
                    enddo
                enddo
            enddo
        enddo
    enddo
enddo
0 Kudos
David-F8
Novice
174 Views

Well, the new approach I alluded to above dropped out quite quickly.

Still single threaded, no longer using any number theory tricks, but down to 1.4s now.

Testing numbers 1...600

    Building 5th powers (0.000 seconds)

  Building power tables (0.000 seconds)

     Building power map (0.016 seconds)

( 27,  84, 110, 133)
( 54, 168, 220, 266)
( 81, 252, 330, 399)
(108, 336, 440, 532)

791 iterations in 1.464 seconds.

And all the solutions for values up to 999 in under 10s.

( 27,  84, 110, 133)
( 54, 168, 220, 266)
( 81, 252, 330, 399)
(108, 336, 440, 532)
(135, 420, 550, 665)
(162, 504, 660, 798)
(189, 588, 770, 931)

 

0 Kudos
David-F8
Novice
92 Views

Final update:

  • 1.1s for all values up to 600.
  • 8.7s for all values up to 999.

 

 

0 Kudos
Reply