Page 4 of 13

Posted: Sun Jun 24, 2007 3:06 am
by Dreamland Fantasy
Hi Michael,

My function for checking palindromes seems to be very slightly quicker than yours.

Running the following code on my system I get an average time of 197.500ms for your routine and 186.110ms for mine.

Code: Select all

; Project Euler - Problem 4
;
; A palindromic Number reads the same both ways. the largest palindrome made
; from the product of two 2-digit numbers is 9009 = 91 × 99.
;
; Find the largest palindrome made from the product of two 3-digit numbers.

#LoopTimes = 100

;- Using Michael Vogel's palindrome routine

Procedure.l MichaelsPalindrome(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

TimeStart = GetTickCount_()

For loop = 1 To #LoopTimes
  
  MichaelsResult = 0
  For a = 100 To 999
    For b = 100 To 999
      Product = a * b
      If MichaelsPalindrome(Str(Product))
        If Product > MichaelsResult
          MichaelsResult = Product
        EndIf
      EndIf
    Next
  Next
  
Next

MichaelsTimeElapsed = GetTickCount_() - TimeStart

;- Using my palindrome routine

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

TimeStart = GetTickCount_()

For loop = 1 To #LoopTimes
  
  MyResult = 0
  For a = 100 To 999
    For b = 100 To 999
      Product = a * b
      If MyPalindrome(Str(Product))
        If Product > MyResult
          MyResult = Product
        EndIf
      EndIf
    Next
  Next
  
Next

MyTimeElapsed = GetTickCount_() - TimeStart

;- Display information

Text$ = "Average time elapsed for Michael's routine: " + StrF(MichaelsTimeElapsed / #LoopTimes, 3)+"ms (Result: " + Str(MichaelsResult) + ")" + Chr(10)
Text$ + "Average time elapsed for my routine: "+StrF(MyTimeElapsed / #LoopTimes, 3)+"ms (Result: " + Str(MyResult) + ")"

MessageRequester("Palindrome Comparison", Text$)
Kind regards,

Francis.

Posted: Sun Jun 24, 2007 5:40 pm
by Demivec
@Dreamland Fantasy,

Here is a different approach to problem 4 that will speed up your solution 1000 times. or in other words make your average time 1.86110 ms.

Change your code(s) from:

Code: Select all

or loop = 1 To #LoopTimes
 
  MyResult = 0
  For a = 100 To 999
    For b = 100 To 999
      Product = a * b
      If MyPalindrome(Str(Product))
        If Product > MyResult
          MyResult = Product
        EndIf
      EndIf
    Next
  Next
 
Next 
to:

Code: Select all

For loop = 1 To #LoopTimes
  a2=100
  MyResult = 0
  For a = 999 To a2 Step -1
    b1=a:b2=a2
    For b = b1 To b2 Step -1
      Product = a * b
      If MyPalindrome(Str(Product))
        If Product > MyResult
          MyResult = Product
          a2=b+1
          If a2>a
            Break 2
          EndIf
          Break
        EndIf
      EndIf
    Next
  Next
  
Next
You thought the challenge was merely to find the highest of all palindromes that were the result of two 3-digit factors, instead you should have been searching for the highest factors that would produce a palindrome. My modification eliminates the wasteful searching of all the possible products from the factors by doing 3 things:
1. Start with the highest factors and work towards the smallest ones.
2. Only check half the products. (i.e 333*555 is the same as 555*333)
3. If a product is a palindrome only look for a palindrome among higher products from then on (limit the range).
This is the first time I have joined this disucussion. I hope I understood the problem correctly. If I misunderstood then my solution is probably not valid. Oops! :)

Posted: Sun Jun 24, 2007 6:19 pm
by Dreamland Fantasy
Hi Demivec,

Thanks for the input! :)

The main loop I posted was more to illustrate the actual speed of the Palindrome() routine rather than the speed of the main loop which re-reading my post probably wasn't clear.

As it stands I felt that the calculation time was satisfactory since it is completed in under a second. That said, the only optimisation I made in my 'final' version was to limit the range from 900 to 999 rather than 100 to 999.

The techniques you suggested (plus a few others) I have already used in some of the other problems where if I was doing it by brute force method would take far too long.

Kind regards,

Francis.

Posted: Sun Jun 24, 2007 6:34 pm
by Dreamland Fantasy
Hi Demivec,

Looking at your solution I noticed an obvious optimisation. Try this code:

Code: Select all

For loop = 1 To #LoopTimes 
  a2=100 
  MyResult = 0 
  For a = 999 To a2 Step -1 
    b1=a:b2=a2 
    For b = b1 To b2 Step -1 
      Product = a * b 
      If Product > MyResult 
        If MyPalindrome(Str(Product)) 
          MyResult = Product 
          a2=b+1 
          If a2>a 
            Break 2 
          EndIf 
          Break 
        EndIf 
      EndIf 
    Next 
  Next 
  
Next
All I have done is swapped two of the lines with If statements around so that you are performing less palindrome checks. With your code it took an average time of 2.262ms and with the modification it took 1.654ms.

Kind regards,

Francis.

Posted: Sun Jun 24, 2007 8:43 pm
by Dreamland Fantasy
Hi Demivec,

I've simplified your code a bit to make it slightly faster.

Code: Select all

For loop = 1 To #LoopTimes
  
  min = 100
  MyResult = 0
  For a = 999 To min Step -1
    For b = a To min Step -1
      Product = a * b
      If Product > Result
        If MyPalindrome(Str(Product))
          MyResult = Product
          min = b + 1
          If min > a
            Break 2
          EndIf
          Break
        EndIf
      EndIf
    Next
  Next
  
Next
The average time now has been reduced to 1.560ms.

Kind regards,

Francis.

Posted: Sun Jun 24, 2007 9:38 pm
by Demivec
Dreamland Fantasy wrote:That said, the only optimisation I made in my 'final' version was to limit the range from 900 to 999 rather than 100 to 999
I think you can only use that range limitation if you already knew what the answer was. That's why I included the full range of unique factors.
Dreamland Fantasy wrote:The average time now has been reduced to 1.560ms.
:shock: Fast!

Posted: Mon Jun 25, 2007 1:27 am
by Dreamland Fantasy
Demivec wrote:I think you can only use that range limitation if you already knew what the answer was. That's why I included the full range of unique factors.
I assumed that there would be a good chance that the answer would be produced by this range of numbers which is why they were chosen. This was supported by the fact that the answer I got was the same as the one expected on the Project Euler website.

If the answer turned out to be wrong I would have expanded the range. :wink:

Kind regards,

Francis.

Posted: Mon Jun 25, 2007 7:20 am
by Michael Vogel
Dreamland Fantasy wrote: Hi Michael,
My function for checking palindromes seems to be very slightly quicker than yours. [...]
Hi Francis,

interesting that your routine is faster, I see only two differences:
o it starts "searching" from the beginning - my procedure starts from the middle
o it checks the whole string (if it is a palindrom) - my procedure checks only "the half" of the string

So yours should be faster, when...
o there are few palindromes and the difference will be at the edge

...and my procedure should be faster, when...
o there are many palindromes or the differences will be in the middle

But it demonstrates, that even small changes are able to influence the running time :roll:

Michael

Btw, I need someone to "tune" my code for problem 110 which actually would run for years (!) until I'll get a result...

Posted: Mon Jun 25, 2007 10:12 am
by Dreamland Fantasy
Hi Michael,

Even more interesting, I have just tried the code to compare palindromes on another computer. Sometimes your code is faster, sometimes mine is faster and sometimes they're neck and neck. :?

Problem 110 does look a doozy :shock:, but I'm not even at that stage yet. The highest problem I've done so far is 56, but I've got a lot of gaps to fill in below that.

Kind regards,

Francis.

Posted: Mon Jun 25, 2007 11:03 am
by Dreamland Fantasy
Michael Vogel wrote:Btw, I need someone to "tune" my code for problem 110 which actually would run for years (!) until I'll get a result...
I haven't tried 110 or 108 which is the easier version, but one thing I did notice is that n seems to be a factor of x * y (based on the information provided in problem 108, I haven't checked any further than this). I don't know if you could use this to simplify things.

Kind regards,

Francis.

Posted: Tue Jun 26, 2007 11:52 am
by Michael Vogel
Dreamland Fantasy wrote: I haven't tried 110 or 108 which is the easier version, but one thing I did notice is that n seems to be a factor of x * y (based on the information provided in problem 108, I haven't checked any further than this). I don't know if you could use this to simplify things.

Kind regards,

Francis.
Thanks Francis, yes there's something like 2*3*5*7*11*... which makes 108 not that complicate (but tricky enough;), but 110 is even worse! And it's the only "X" in my completion list (http://projecteuler.net/index.php?secti ... file=11160) for now :evil:

Posted: Tue Jun 26, 2007 12:09 pm
by Dreamland Fantasy
Michael Vogel wrote:
Dreamland Fantasy wrote: I haven't tried 110 or 108 which is the easier version, but one thing I did notice is that n seems to be a factor of x * y (based on the information provided in problem 108, I haven't checked any further than this). I don't know if you could use this to simplify things.

Kind regards,

Francis.
Thanks Francis, yes there's something like 2*3*5*7*11*... which makes 108 not that complicate (but tricky enough;), but 110 is even worse! And it's the only "X" in my completion list (http://projecteuler.net/index.php?secti ... file=11160) for now :evil:
You're still doing a lot better than me at the moment! http://projecteuler.net/index.php?secti ... file=12632

Kind regards,

Francis.

Posted: Tue Jun 26, 2007 10:07 pm
by Demivec
@Dreamland Fantasy:

I've been experimenting with regard to gaining the most speed out of the palindrome routines (to enhance my skills with PB). I've made a few more increases over the routines you posted last. I also did a version for testing palindromes of length 4 or 5 only by unrolling the loops.

This is what I came up with for the general case (palindromes of any length>1) and the specific case in Problem 4:

Code: Select all

; Project Euler - Problem 4
;
; A palindromic Number reads the same both ways. the largest palindrome made
; from the product of two 2-digit numbers is 9009 = 91 × 99.
;
; Find the largest palindrome made from the product of two 3-digit numbers.

#LoopTimes = 100

;- Using Demivec's palindrome routine
Procedure.l DemivecsPalindrome(A$)
  Protected I,length=Len(A$)
    len2=length>>1
    len3=length-1
    For I=len2-1 To 0 Step -1
      If (PeekB(@A$+I)<>PeekB(@A$+len3-I))
        ProcedureReturn #False
      EndIf
    Next
    ProcedureReturn #True
EndProcedure

TimeStart = GetTickCount_()
For loop = 1 To #LoopTimes
  
  min = 100
  DemResult = 0
  For a = 999 To min Step -1
    For b = a To min Step -1
      Product = a * b
      If Product > DemResult
        If DemivecsPalindrome(Str(Product))
          DemResult = Product
          min = b + 1
          If min > a
            Break 2
          EndIf
          Break
        EndIf
      EndIf
    Next
  Next
  
Next
DemTimeElapsed = GetTickCount_() - TimeStart

Procedure.l DemivecsPalindrome2(A$) ;only test palindromes of length 5 or 6
  Protected length=Len(A$)
  If length=6
    If (PeekB(@A$)=PeekB(@A$+5)) And (PeekB(@A$+1)=PeekB(@A$+4)) And (PeekB(@A$+2)=PeekB(@A$+3))
      ProcedureReturn #True
    Else
      ProcedureReturn #False
    EndIf
  Else
    If (PeekB(@A$)=PeekB(@A$+4)) And (PeekB(@A$+1)=PeekB(@A$+3))
      ProcedureReturn #True
    Else
      ProcedureReturn #False
    EndIf
  EndIf
EndProcedure

TimeStart = GetTickCount_()
For loop = 1 To #LoopTimes
  
  min = 100
  DemResult2 = 0
  For a = 999 To min Step -1
    For b = a To min Step -1
      Product = a * b
      If Product > DemResult2
        If DemivecsPalindrome2(Str(Product))
          DemResult2 = Product
          min = b + 1
          If min > a
            Break 2
          EndIf
          Break
        EndIf
      EndIf
    Next
  Next
  
Next
DemTimeElapsed2 = GetTickCount_() - TimeStart

Text$ + "Average time elapsed for Demivec's routine: "+StrF(DemTimeElapsed / #LoopTimes, 3)+"ms (Result: " + Str(DemResult) + ")"+ Chr(10)
Text$ + "Average time elapsed for Demivec's routine2: "+StrF(DemTimeElapsed2 / #LoopTimes, 3)+"ms (Result: " + Str(DemResult2) + ")"

MessageRequester("Palindrome Comparison", Text$)
The routine to test the general case test faster on my machine, will you test it on yours to verify? The palindrome routines are used in several of the Problems and I think to have them operate as quickly as possible would definately be helpful (though for problem 4 it's a bit of an overkill :wink: )

Posted: Tue Jun 26, 2007 10:57 pm
by Dreamland Fantasy
Hi demivec,

I'm using my laptop here rather than my main computer, but I get 1.766ms for my routine, 1.812ms for Demivec routine and 1.718ms for Demivec routine 2. Unfortunately these results may be skewed by the CPU throttling between clock speeds.

I'll re-run them on my main computer when I get a chance.

Kind regards,

Francis.

Posted: Thu Jun 28, 2007 7:54 pm
by Michael Vogel
Dreamland Fantasy wrote: [...]at the moment! http://projecteuler.net/index.php?secti ... file=12632
Hi Francis, you're completing the problems very fast!

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

Michael.