Page 5 of 13

Posted: Thu Jun 28, 2007 9:57 pm
by Dreamland Fantasy
Michael Vogel wrote:Hi Francis, you're completing the problems very fast!
:D
Michael Vogel wrote:Hi Demivec, maybe you can try to solve problem 125 - it has to do also with palindromes and seems also not to be too complicate to solve...
That one was surprisingly easy to complete! I managed to get a working solution (although it could do with a bit of optimising) in less than 10 minutes! :)

Kind regards,

Francis.

Posted: Fri Jun 29, 2007 7:24 am
by Michael Vogel
Dreamland Fantasy wrote: That one was surprisingly easy to complete! I managed to get a working solution (although it could do with a bit of optimising) in less than 10 minutes! :)
Cool :!:

That's one still missing on my list :oops: - will try that today :?

Posted: Fri Jun 29, 2007 9:10 am
by Dreamland Fantasy
I'm still trying to optimise problem 125 as it currently takes just over a minute to complete on my laptop. :?

Kind regards,

Francis.

Posted: Fri Jun 29, 2007 4:58 pm
by Demivec
Michael Vogel wrote:Hi Demivec, maybe you can try to solve problem 125 - it has to do also with palindromes and seems also not to be too complicate to solve...
Ok, I bit the bullet, stepped into Eulerville, registered and solved problem 125. :)
Dreamland Fantasy wrote:That one was surprisingly easy to complete! I managed to get a working solution (although it could do with a bit of optimising) in less than 10 minutes! Smile
Dreamland Fantasy wrote:I'm still trying to optimise problem 125 as it currently takes just over a minute to complete on my laptop
My version ran in 8.063 seconds. :shock:

Posted: Fri Jun 29, 2007 8:30 pm
by Dreamland Fantasy
Demivec wrote:
Dreamland Fantasy wrote:I'm still trying to optimise problem 125 as it currently takes just over a minute to complete on my laptop
My version ran in 8.063 seconds. :shock:
I've managed to get mine down from 63 seconds to under 35 seconds.

The reason mine is so slow is probably due to the way I approached the problem which was to go through all the numbers under 10^8, check if they were a palindrome (which is where my algorithm spends about 95% of its time) and then check if it has a valid consecutive number sequence. On the plus side doing it this way means that you don't need to bother about checking for duplicates they way most of the guys on the Project Euler forum have.

To get mine as fast as yours I would need to rewrite the algorithm so that I'm not going through all of the potential numbers.

Incidentally, I found a marginally faster way (from my original) of checking for palindromes.

Code: Select all

Procedure IsPalindrome(A$)
  
  Protected i, length = Len(A$) - 1

  For i = 0 To length
    If PeekB(@A$ + i) = PeekB(@A$ + length - i)
    Else
      ProcedureReturn 0
    EndIf
  Next
  
  ProcedureReturn 1
  
EndProcedure
The only change I made was to use '=' instead of '<>' in the if statement and then do a procedure return on the else statement.

I suspect that doing it this way might be faster because you are not doing two checks (the '<' and '>') and are thus reducing the overheads.

Kind regards,

Francis.

Posted: Fri Jun 29, 2007 8:55 pm
by Demivec
Dreamland Fantasy wrote:To get mine as fast as yours I would need to rewrite the algorithm so that I'm not going through all of the potential numbers
I generated the legal palindromes and then checked which ones had valid sums. The sums I checked with pre-computed squares.
By only checking the palindromes for sums, I narrowed the possibilities to only 0.02% of the range of numbers and saved the time of actually verifying if a number was a palindrome.

Posted: Fri Jun 29, 2007 10:07 pm
by Dreamland Fantasy
Demivec wrote:
Dreamland Fantasy wrote:To get mine as fast as yours I would need to rewrite the algorithm so that I'm not going through all of the potential numbers
I generated the legal palindromes and then checked which ones had valid sums. The sums I checked with pre-computed squares.
By only checking the palindromes for sums, I narrowed the possibilities to only 0.02% of the range of numbers and saved the time of actually verifying if a number was a palindrome.
Ah, so you've more-or-less done it the same way I did except you've pre-computed the valid palindromes.

How long does it take you to pre-compute the valid palindromes?

Kind regards,

Francis.

Posted: Fri Jun 29, 2007 10:38 pm
by Dreamland Fantasy
I've just redone the program using precomputed palindromes.

It completed in just 1.3 seconds!

Kind regards,

Francis.

Posted: Fri Jun 29, 2007 11:11 pm
by Michael Vogel
Quite fast!

As my first version took to long - I take a different approach just calculating squares and checking if the sum is a palindrome - that takes now around 3 1/2 second :oops: (but my notebook is very slow :P )

Michael

So what's about 110 and 118 these two are too hard for me (for problem 118 I got after a very long calculation time the result 21192 which is... wrong :cry: )

Posted: Fri Jun 29, 2007 11:26 pm
by Dreamland Fantasy
Well, here is my code. To be honest I think precalculating the palindromes and squares (and not including them in the time taken) is cheating slightly. :?

Code: Select all

#TITLE = "Project Euler - Problem 125"

Procedure PrecalculateSquares()

  Protected n = 10000, i
  Global Dim Square.q(n)

  For i = 1 To n
    Square(i) = i * i
  Next

EndProcedure

Procedure IsPalindrome(A$)
  
  Protected i, length = Len(A$) - 1
  
  For i = 0 To length
    If PeekB(@A$ + i) = PeekB(@A$ + length - i)
    Else
      ProcedureReturn 0
    EndIf
  Next
  
  ProcedureReturn 1
  
EndProcedure

Procedure IsConsecutiveSum(n.q)
  
  Protected Sum.q, HighNumber = n >> 1
  HighNumber = Sqr(HighNumber) + 1
  Protected LowNumber = HighNumber
  
  While Sum < n And LowNumber
    Sum + Square(LowNumber)
    If Sum > n
      Sum - Square(HighNumber)
      HighNumber - 1
    EndIf
    LowNumber - 1
  Wend
  
  If Sum = n
    If HighNumber <> LowNumber + 1
      ProcedureReturn 1
    EndIf
  EndIf
  
EndProcedure

Procedure PrecalculatePalindromes(n.q)
  
  Protected i, j=0
  Global Dim PalindromeList.q(20000)
  
  For i = 1 To n
    If IsPalindrome(Str(i))
      PalindromeList(j) = i
      j + 1
    EndIf
  Next
  
  ProcedureReturn j
  
EndProcedure

MaxNumber.q = Pow(10, 8) - 1
Result.q = 0

PrecalculateSquares()
NoofPalindromes = PrecalculatePalindromes(MaxNumber)

TimeStarted = GetTickCount_()

For i = 0 To NoofPalindromes
  If IsConsecutiveSum(PalindromeList(i))
    Result + PalindromeList(i)
  EndIf
Next

TimeElapsed = GetTickCount_() - TimeStarted

MessageText$ = "Result = " + StrQ(Result) + Chr(10) + Chr(10)
MessageText$ + "Average time taken: " + Str(TimeElapsed)+"ms"

MessageRequester(#TITLE, MessageText$)
I would love to hear any suggestions that you have to improve it speedwise.

Kind regards,

Francis

Posted: Fri Jun 29, 2007 11:33 pm
by Dreamland Fantasy
Michael Vogel wrote:So what's about 110 and 118 these two are too hard for me (for problem 118 I got after a very long calculation time the result 21192 which is... wrong :cry: )
I don't think I'm ready to tackle 110 yet, but 118 looks easy enough (although looks can be very deceiving sometimes!).

I'll give 118 a try and see what I come up with.

Kind regards,

Francis.

Posted: Sat Jun 30, 2007 4:21 pm
by Demivec
Dreamland Fantasy wrote:Well, here is my code. To be honest I think precalculating the palindromes and squares (and not including them in the time taken) is cheating slightly. :?
Note I didn't say "I precalculated the palindromes," I said "I generated the legal palindromes." I also included all steps in the solution (precalculated or otherwise) in the timing. Here's my solution:

Code: Select all

; Project Euler - Problem 125
;
;Find the sum of all the numbers less than 10^8 that are both
;palindromic And can be written As the sum of consecutive squares.

;range of possible factors(2 or more that are consecutive): 1-7072

#Title="Euler Problem 125"

#MaxFactor=7072 ;maximum factor possible
#MinFactor=1 ;minimum factor possible

StartTime=ElapsedMilliseconds()

;-setup pre-calculated squares of possible factors
Global Dim square.l(#MaxFactor)

For a = #MinFactor To #MaxFactor
  square(a) = Pow(a,2)
Next

;-procedures
Procedure.l validateSum(target.l) ;returns 0 if not valid, or target if valid
  Protected sum.l=square(1),factor.l=1, highFactor.l
  
  Repeat ;look for a sum of 1 to factor
    factor+1
    sum+square(factor)
    If sum>target
      Break
    EndIf
    If sum=target
      ProcedureReturn target
    EndIf
  ForEver
  
  highFactor=factor
  Repeat ;look for sums of factor to highFactor
    sum=square(highFactor)
    If (sum>target) Or (sum+square(highFactor-1)>target)
      ProcedureReturn 0
    EndIf
    factor=highFactor
    Repeat
      factor-1
      sum+square(factor)
      If sum>target
        Break
      EndIf
      If sum=target
        ProcedureReturn target
      EndIf
    ForEver
    highFactor+1  
  ForEver
EndProcedure

Procedure.l CreatePalindrome(a,Odd.l) ;Odd=1 if palindrome will have an odd# of digits
  If Odd<>1
    Odd=0 
  EndIf
  
  Protected A$=Str(a),length.l=Len(A$),*A=@A$
  Protected palindrome$=A$+Left(A$,length-Odd),*P=@palindrome$+Len(palindrome$)-1,I.l

  For I=0 To length-1-Odd
    PokeB(*P-I,PeekB(*A+I))
  Next
  ProcedureReturn Val(palindrome$)
EndProcedure

;-Solution search
result.q=0
;check each possible palindrome, instead of each possible value

;search multiple digit palindromes up to 10^8
For a=0 To 3 ;used to calculate range for generation of palindromes
  lowRange=Pow(10,a)
  highRange=Pow(10,a+1)-1
  For b=1 To 0 Step -1 ;odd/even indicator
    For c=lowRange To highRange
      result+validateSum(CreatePalindrome(c,b))
    Next 
  Next 
Next 
 
timeElapsed=ElapsedMilliseconds()-StartTime  

text$= "Time to find solution: "+StrF(timeElapsed, 3)+"ms (Result: " + StrQ(result) + ")"
MessageRequester(#Title, text$)
This finishes in 8.032 seconds.

Posted: Sat Jun 30, 2007 4:44 pm
by Dreamland Fantasy
Demivec wrote:
Dreamland Fantasy wrote:Well, here is my code. To be honest I think precalculating the palindromes and squares (and not including them in the time taken) is cheating slightly. :?
Note I didn't say "I precalculated the palindromes," I said "I generated the legal palindromes." I also included all steps in the solution (precalculated or otherwise) in the timing.
Ah, I've misunderstood what you meant. Very ingenious solution! :)

Kind regards,

Francis.

Posted: Sat Jun 30, 2007 5:08 pm
by Dreamland Fantasy
Hi Demivec,

I've just substituted my IsConsecutiveSum() routine into your program. It now completes in 1.3 seconds instead of 7.9 seconds on my laptop! 8)

Code: Select all

; Project Euler - Problem 125 
; 
;Find the sum of all the numbers less than 10^8 that are both 
;palindromic And can be written As the sum of consecutive squares. 

;range of possible factors(2 or more that are consecutive): 1-7072 

#Title="Euler Problem 125" 

#MaxFactor=7072 ;maximum factor possible 
#MinFactor=1 ;minimum factor possible 

StartTime=ElapsedMilliseconds() 

;-setup pre-calculated squares of possible factors 
Global Dim square.l(#MaxFactor) 

For a = #MinFactor To #MaxFactor 
  square(a) = Pow(a,2) 
Next 

;-procedures 
Procedure.q IsConsecutiveSum(n.l)
  
  Protected sum.q, HighNumber = n >> 1
  HighNumber = Sqr(HighNumber) + 1
  Protected LowNumber = HighNumber
  
  While sum < n And LowNumber
    sum + square(LowNumber)
    If sum > n
      sum - square(HighNumber)
      HighNumber - 1
    EndIf
    LowNumber - 1
  Wend
  
  If sum = n
    If HighNumber <> LowNumber + 1
      ProcedureReturn n
    EndIf
  EndIf
  
EndProcedure

Procedure.l CreatePalindrome(a,Odd.l) ;Odd=1 if palindrome will have an odd# of digits 
  If Odd<>1 
    Odd=0 
  EndIf 
  
  Protected A$=Str(a),length.l=Len(A$),*A=@A$ 
  Protected palindrome$=A$+Left(A$,length-Odd),*P=@palindrome$+Len(palindrome$)-1,I.l 
  
  For I=0 To length-1-Odd 
    PokeB(*P-I,PeekB(*A+I)) 
  Next 
  
  ProcedureReturn Val(palindrome$) 
EndProcedure 

;-Solution search 
result.q=0 
;check each possible palindrome, instead of each possible value 

;search multiple digit palindromes up to 10^8 
For a=0 To 3 ;used to calculate range for generation of palindromes 
  lowRange=Pow(10,a) 
  highRange=Pow(10,a+1)-1 
  For b=1 To 0 Step -1 ;odd/even indicator 
    For c=lowRange To highRange 
      result+IsConsecutiveSum(CreatePalindrome(c,b)) 
    Next 
  Next 
Next 
  
timeElapsed=ElapsedMilliseconds()-StartTime  

text$= "Time to find solution: "+StrF(timeElapsed, 3)+"ms (Result: " + StrQ(result) + ")" 
MessageRequester(#Title, text$)
Kind regards,

Francis.

Posted: Sat Jun 30, 2007 7:28 pm
by Michael Vogel
Hi,
my notebook seems to be very lazy (takes 6 seconds to finish Francis' code), so maybe my solution could be fast on your machines:

Code: Select all

	Procedure Stopuhr(n)
		Global Timer=GetTickCount_()
		Global Problemcode=n
	EndProcedure
	Procedure ShowString(x.s)
		MessageRequester("Calc time "+Str(GetTickCount_()-Timer)+"ms","Problem "+Str(Problemcode)+#CR$+"= "+x)
		SetClipboardText(x)
	EndProcedure
	Procedure ShowResult(x.q)
		ShowString(StrQ(x))
	EndProcedure
Procedure.l Palindrom(x.s)
		Protected l=Len(x)-1
		Protected i=l>>1
		While i>=0
			If PeekB(@x+i)<>PeekB(@x+l-i)
				ProcedureReturn #False
			EndIf
			i-1
		Wend
		ProcedureReturn #True
	EndProcedure
	
Procedure Problem_125()

	; The palindromic number 595 is interesting because it can be written as the sum
	; of consecutive squares: 6^2 + 7^2 + 8^2 + 9^2 + 10^2 + 11^2 + 12^2.

	; There are exactly eleven palindromes below one-thousand that can be written as
	; consecutive square sums, and the sum of these palindromes is 4164. Note that
	; 1 = 0^2 + 1^2 has not been included as this problem is concerned with the squares
	; of positive integers.

	; Find the sum of all the numbers less than 10^8 that are both palindromic and can
	; be written as the sum of consecutive squares.

	Stopuhr(125)

	#Grenze=100000000

	#maxA=4999
	#maxB=5000

	NewList zahlen.q()
	AddElement(zahlen())
	zahlen()=0; bleibt am Listenende...

	found=0

	s.q=0
	a.q
	b.q

	a=0
	While a<#maxA
		a+1
		b=a
		s=a*a
		While b<#maxB
			b+1
			s+b*b
			If s<#Grenze
				If Palindrom(StrQ(s))
					FirstElement(zahlen())
					While (zahlen()<s) And (zahlen()>0)
						NextElement(zahlen())
					Wend
					If zahlen()<>s
						InsertElement(zahlen())
						zahlen()=s
					EndIf
				EndIf
			EndIf
		Wend
	Wend

	s=0
	If FirstElement(zahlen())
		Repeat
			a=zahlen()
			If (a>0) And (a<#Grenze)
				Debug a
				s+a
			EndIf
			NextElement(zahlen())
		Until a=0
	EndIf

	ShowResult(s)

EndProcedure

Problem_125()
I think, its quite interesting how many different approaches are possible - we are "only" three programmer and everyone found a (totaly) personal way to implement the thing :wink:

And btw I agree also, that precalculation (of squares etc) is not cheating, it's part of a strategy (and part of the total calculation time;)

Michael