- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
Re: Breaking for loops based on found results
Fri, 2009-04-03, 13:54
Ah, that looks neat. It's a bit slower than mine, but for the short
strings used it shouldn't make a difference.
I did figure out a good way off doing it. Although it doesn't
terminate fully early, it skips a lot of calculation and iterations:
var max = 0
val palindromes = for {
a <- 100 to 999 reverse;
if (a*999 > max);
b <- a to 999 reverse;
c = a*b;
if (c > max && isPalindrome(c))
} yield {max=c;max}
I wrote a full article at
http://blog.staale.org/2009/04/euler-problem-4-in-scala.html
On Fri, Apr 3, 2009 at 2:19 PM, Vadim wrote:
> Don't know about early for-comprehension termination, but you can simplify
> your isPalindrome like this:
>
> def isPalindrome(chars: String): Boolean = chars.reverse.mkString == chars
>
>
> On Fri, Apr 3, 2009 at 11:12 AM, Ståle Undheim wrote:
>>
>> When I am trying to learn a new language, I often go through stuff at
>> projecteuler.net. I haven't had too much time, so only at problem 4 in
>> scala:
>>
>> """
>> 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.
>> """
>> The problem is easy enough, here is my isPalindrome method:
>> object Util {
>> def isPalindrome(txt: String, s:Int, e:Int):Boolean = {
>> if (s>e) true
>> else if (txt(s) == txt(e)) isPalindrome(txt,s+1,e-1)
>> else false
>> }
>> def isPalindrome(txt:String): Boolean =
>> isPalindrome(txt,0,txt.length-1)
>> implicit def toStr(n:Int): String = n.toString
>> }
>>
>> And here is my solution:
>> object Euler004 extends Application {
>> val palindromes = for {
>> a <- 100 to 999 reverse;
>> b <- a to 999 reverse;
>> if (isPalindrome(a*b))
>> } yield a*b
>> val results = palindromes.reduceRight((a,b)=> if (a>b) a else b)
>> println(results)
>> }
>>
>> The problem here is that the 3rd result I find is actually the correct
>> one, but I end up finding all the palindromes when I just want to find
>> the largest. What I want here is that if a*b is a palindrome, I don't
>> need to examine smaller values of b, and if a*999 is smaller than the
>> biggest palindrome, I don't need to examine further. And if a*b is
>> smaller than the biggest palindrome, I don't need to examine further
>> values of b.
>>
>> Are there ways to implement checks like this? Is there a functional
>> and short way to solve this, or do I need to rewrite to using while
>> loops?
>
>
Mon, 2009-04-06, 02:57
#2
Re: Breaking for loops based on found results
Seth Tisue wrote:
>>>>>> Ståle> The problem here is that the 3rd result I find is actually the
>>>>>> Ståle> correct one, but I end up finding all the palindromes when I
>>>>>> Ståle> just want to find the largest. What I want here is that if a*b
>>>>>> Ståle> is a palindrome, I don't need to examine smaller values of b,
>>>>>> Ståle> and if a*999 is smaller than the biggest palindrome, I don't
>>>>>> Ståle> need to examine further.
>>>>>>
>>>>>> It's better to check a*a instead of a*999.
>>>>>>
>>>>>>
Seth,
Actually a*a doesn't result in the correct answer. Using a*999 > max
asks the question is there some value in the range a - 999 that might
get me a larger palindrome. Testing for a^2 > max aborts too many
possible values.
Anyway neither of those tests results in a minimum of calls to
isPalindrome. The code below gets the minimum number of tests so far.
The intent is to only test what's necessary to finding minimum values
for the b loop works and is a little more intuitive for me. (actually
keeping the other a*999 < max results in 1 less test but adds a line of
code).
Is there a way to inline minb in the code below? I tried a couple of
things but they didn't work.
def minb(x:Int, y:Int) = if (x == 0) y else (x / y).toInt
var max = 0
var tests = 0;
val palindromes = for {
a <- 100 to 999 reverse;
b <- minb(max, a) to 999 reverse;
c = a*b;
val t:Unit = {tests = tests + 1;}
if (c> max && isPalindrome(c.toString()))
} yield {max=c;max}
println("tests: " + tests)
Tue, 2009-04-07, 20:17
#3
Re: Breaking for loops based on found results
>>>>> "Brett" == Brett Knights writes:
>>>>>>> It's better to check a*a instead of a*999.
Brett> Actually a*a doesn't result in the correct answer.
You're right. I was looking at my own code and hadn't noticed it
differed from yours in a crucial respect.
Brett> Is there a way to inline minb in the code below? I tried a
Brett> couple of things but they didn't work.
b <- (if(max == 0) a else (max / a)) to 999 reverse
should work, I think
>>>>> "Ståle" == Ståle Undheim writes:
Ståle> The problem here is that the 3rd result I find is actually the
Ståle> correct one, but I end up finding all the palindromes when I
Ståle> just want to find the largest. What I want here is that if a*b
Ståle> is a palindrome, I don't need to examine smaller values of b,
Ståle> and if a*999 is smaller than the biggest palindrome, I don't
Ståle> need to examine further.
It's better to check a*a instead of a*999.
Ståle> Are there ways to implement checks like this? Is there a
Ståle> functional and short way to solve this, or do I need to rewrite
Ståle> to using while loops?
And here's a short, pure-functional solution that is efficient
(takes all three early exits you identified):
def loop1(a:Int,winner:Int):Int =
if(a < 100 || a * a <= winner) winner
else {
def loop2(p:Int):Int =
if(p <= winner) loop1(a - 1,winner)
else if(isPalindrome(p)) loop1(a - 1,p)
else loop2(p - a)
loop2(a * a)
}
loop1(999,0)
Looks something you'd write in your freshman Scheme class, eh?
For comparison, here's the while version:
var winner = 0
var a = 999
while(a >= 100) {
var p = a * a
if(p <= winner) return winner
while(p > winner) {
if(isPalindrome(p)) { winner = p; p = 0 }
else p -= a
}
a -= 1
}
winner
I do not see any elegant way to solve this using for or higher-order
functions while remaining purely functional and taking all three early
exits.
Of all the solutions, I think the one you posted that uses for and a
single var is the most readable.