3

I'm trying to find all palindromic sequences of length k that sum to n. I have a specific example (k=6):

def brute(n):
J=[]
for a in range(1,n):
    for b in range(1,n):
        for c in range(1,n):
            if (a+b+c)*2==n:
                J.append((a,b,c,c,b,a))
return(J)

The output gives me something like:

[(1, 1, 6, 6, 1, 1),
 (1, 2, 5, 5, 2, 1),
 (1, 3, 4, 4, 3, 1),
 (1, 4, 3, 3, 4, 1),
 (1, 5, 2, 2, 5, 1),
 (1, 6, 1, 1, 6, 1),
 (2, 1, 5, 5, 1, 2),
 (2, 2, 4, 4, 2, 2),
 (2, 3, 3, 3, 3, 2),
 (2, 4, 2, 2, 4, 2),
 (2, 5, 1, 1, 5, 2),
 (3, 1, 4, 4, 1, 3),
 (3, 2, 3, 3, 2, 3),
 (3, 3, 2, 2, 3, 3),
 (3, 4, 1, 1, 4, 3),
 (4, 1, 3, 3, 1, 4),
 (4, 2, 2, 2, 2, 4),
 (4, 3, 1, 1, 3, 4),
 (5, 1, 2, 2, 1, 5),
 (5, 2, 1, 1, 2, 5),
 (6, 1, 1, 1, 1, 6)]

The issue is that I have no idea how to generalize this to any values of n and k. I hear that dictionaries would be helpful. Did I mention I was new to python? any help would be appreciated

thanks

jonan
  • 217
  • 1
  • 8
  • Are you not considering `0` as a possible element? For example, your current solution doesn't find `(0, 0, 8, 8, 0, 0)` (among other possibilities). Also, I assume `k` can be odd. – Matt Messersmith Nov 28 '18 at 16:58
  • Sorry i should have been more clear. I am not considering 0 as a possible element. And yes, I would like to generalize this to any k and n. – jonan Nov 28 '18 at 18:29

1 Answers1

1

The idea is that we simply count from 0 to 10**k, and consider each of these "integers" as a palindrome sequence. We left pad with 0 where necessary. So, for k==6, 0 -> [0, 0, 0, 0, 0, 0], 1 -> [0, 0, 0, 0, 0, 1], etc. This enumerates over all possible combinations. If it's a palindrome, we also check that it adds up to n.

Below is some code that (should) give a correct result for arbitrary n and k, but is not terribly efficient. I'll leave optimizing up to you (if it's necessary), and give some tips on how to do it.

Here's the code:

def find_all_palindromic_sequences(n, k):
    result = []
    for i in range(10**k):
        paly = gen_palindrome(i, k, n)
        if paly is not None:
            result.append(paly)
    return result

def gen_palindrome(i, k, n):
    i_padded = str(i).zfill(k)
    i_digits = [int(digit) for digit in i_padded]
    if i_digits == i_digits[::-1] and sum(i_digits) == n:
        return i_digits

to test it, we can do:

for paly in find_all_palindromic_sequences(n=16, k=6):
    print(paly)

this outputs:

[0, 0, 8, 8, 0, 0]
[0, 1, 7, 7, 1, 0]
[0, 2, 6, 6, 2, 0]
[0, 3, 5, 5, 3, 0]
[0, 4, 4, 4, 4, 0]
[0, 5, 3, 3, 5, 0]
[0, 6, 2, 2, 6, 0]
[0, 7, 1, 1, 7, 0]
[0, 8, 0, 0, 8, 0]
[1, 0, 7, 7, 0, 1]
[1, 1, 6, 6, 1, 1]
[1, 2, 5, 5, 2, 1]
[1, 3, 4, 4, 3, 1]
[1, 4, 3, 3, 4, 1]
[1, 5, 2, 2, 5, 1]
[1, 6, 1, 1, 6, 1]
[1, 7, 0, 0, 7, 1]
[2, 0, 6, 6, 0, 2]
[2, 1, 5, 5, 1, 2]
[2, 2, 4, 4, 2, 2]
[2, 3, 3, 3, 3, 2]
[2, 4, 2, 2, 4, 2]
[2, 5, 1, 1, 5, 2]
[2, 6, 0, 0, 6, 2]
[3, 0, 5, 5, 0, 3]
[3, 1, 4, 4, 1, 3]
[3, 2, 3, 3, 2, 3]
[3, 3, 2, 2, 3, 3]
[3, 4, 1, 1, 4, 3]
[3, 5, 0, 0, 5, 3]
[4, 0, 4, 4, 0, 4]
[4, 1, 3, 3, 1, 4]
[4, 2, 2, 2, 2, 4]
[4, 3, 1, 1, 3, 4]
[4, 4, 0, 0, 4, 4]
[5, 0, 3, 3, 0, 5]
[5, 1, 2, 2, 1, 5]
[5, 2, 1, 1, 2, 5]
[5, 3, 0, 0, 3, 5]
[6, 0, 2, 2, 0, 6]
[6, 1, 1, 1, 1, 6]
[6, 2, 0, 0, 2, 6]
[7, 0, 1, 1, 0, 7]
[7, 1, 0, 0, 1, 7]
[8, 0, 0, 0, 0, 8]

Which looks similar to your result, plus the results that contain 0.

Ideas for making it faster (this will slow down a lot as k becomes large):

  1. This is an embarrassingly parallel problem, consider multithreading/multiprocessing.

  2. The palindrome check of i_digits == i_digits[::-1] isn't as efficient as it could be (both in terms of memory and CPU). Having a pointer at the start and end, and traversing characters one by one till the pointers cross would be better.

  3. There are some conditional optimizations you can do on certain values of n. For instance, if n is 0, it doesn't matter how large k is, the only palindrome will be [0, 0, 0, ..., 0, 0]. As another example, if n is 8, we obviously don't have to generate any permutations with 9 in them. Or, if n is 20, and k is 6, then we can't have 3 9's in our permutation. Generalizing this pattern will pay off big assuming n is reasonably small. It works the other way, too, actually. If n is large, then there is a limit to the number of 0s and 1s that can be in each permutation.

  4. There is probably a better way of generating palindromes than testing every single integer. For example, if we know that integer X is a palindrome sequence, then X+1 will not be. It's pretty easy to show this: the first and last digits can't match for X+1 since we know they must have matched for X. You might be able to show that X+2 and X+3 cannot be palindromes either, etc. If you can generalize where you must test for a new palindrome, this will be key. A number theorist could help more in this regard.

HTH.

Matt Messersmith
  • 12,939
  • 6
  • 51
  • 52
  • Works great! Thanks so much, i'll try implementing some of those ideas to make it quicker. – jonan Nov 28 '18 at 23:32
  • I guess this method doesn't consider sequences whose elements are greater than 9. Any thoughts on how that could be done? – jonan Nov 29 '18 at 00:44
  • 1
    @jonan you can do the same thing, but you have to extend your number base and do it in that base. So, if you had 16 values in your alphabet, then you can use the algorithm above, but you'll be counting up in hex (instead of base 10) – Matt Messersmith Nov 29 '18 at 12:24
  • 1
    @jonan also, consider upvoting this answer if you found it useful! – Matt Messersmith Nov 29 '18 at 12:26
  • i didnt have the ability to upvote at the time but now i do. Gorgeous responses, thanks again! – jonan Nov 29 '18 at 13:45