Find the largest palindrome made from the product of two 3digit numbers.
And the last scratch for now. It is possible to prove that 11 divides a palindromic number. Indeed,
and here
is a multiple of 11 (divisibility by 11 criterion).
The factor 11 can belong to a  and in this case we step just 1 in b. But if 11 doesn't divide a, then we can increase b by 11 each time.

(define (findlargestpalindromeusing11 k)

(let ([m ( (^ 10 k) 1)] [m/10 (^ 10 ( k 1))])

(let ([m11 (* 11 (div m 11))])

(define (iter a b largestpalindrome)

(if (< a m/10) largestpalindrome

(let ([step (if (= 0 (mod a 11)) 1 11)]

[nexta11? (= 0 (mod ( a 1) 11))])

(if (< b m/10) (iter ( a 1) (if nexta11? m m11) largestpalindrome)

(let ([n (* a b)])

(if (<= n largestpalindrome) (iter ( a 1) m largestpalindrome)

(if (palindromenumber? n) (iter ( a 1) m n)

(iter a ( b step) largestpalindrome))))))))

(iter m m 0))))
This speeds up the previous result around ten times, leaving an asymptotic behavior the same. The memory use is the same O(1).
Let's look at results:
k = 2 => N = 9009
k = 3 => N = 906609
k = 4 => N = 99000099
k = 5 => N = 9966006699
k = 6 => N = 999000000999
k = 7 => N = 99956644665999
k = 8 => N = 9999000000009999
...
We could improve our algo drastically, if proven that the soughtfor palindrome is less or equal (and mirrored). I have the feeling that for even k it is equal. But I don't know how to prove it. (I calculated for k = 10 and this does not hold, N = 99999834000043899999).
Find the largest palindrome made from the product of two 3digit numbers.
An author, however, advises a simpler approach. As we are looking for a palindrome a*b, let's iterate a and b in a topdown direction. After finding some palindrome, impose it as a top boundary for palindromes, that is, iterating in the inner loop for b, we stop when a*b cannot be large than that anymore. If we found a new palindrome, it will replace the boundary. Stop condition is finishing the outer loop in a, i.e. when it drops to 2digits number (k1, generally speaking).

(define (findlargestpalindromewithcutoffs k)

(let ([m ( (^ 10 k) 1)] [m/10 (^ 10 ( k 1))])

(define (iter a b largestpalindrome)

(if (< a m/10) largestpalindrome

(if (< b m/10) (iter ( a 1) m largestpalindrome)

(let ([n (* a b)])

(if (<= n largestpalindrome) (iter ( a 1) m largestpalindrome)

(if (palindromenumber? n) (iter ( a 1) m n)

(iter a ( b 1) largestpalindrome)))))))

(iter m m 0)))
Complexity in memory now is just O(1). Performance complexity by my impression is better than in the previous variant. The outer loop has n  n/10 steps, so it cannot be less than O(n). Assuming that a desired palindrome (left half of it, actually) lies close to (which should be proved, strictly speaking), we make no more than operations until find it, and no more than the same afterwards.
This is the worst case, however, and I hope that we find some worsethanideal palindrome quick enough. Suppose, we can use the estimate ab origin, i.e. the inequality holds, where f = n  a, g = n  b. Then we can calculate an estimate of operations as area under a curve y = n / x:
So, the actual algo performance is between
and
.