2

So I received a challenge that states the following: "Design a program that takes as input a 9 digit number where no digit appears twice and produces as output an arrangement of the same 9 digits corresponding to the next highest number. If no such number exists, the algorithm should indicate this. So for example, if the input is 781623954 the output would be 781624359."

So I came up with this idea to flip the indexes, so check the last index with the one right before to see which is bigger and compare then flip if necessary but for some reason my code isn't working. I only did the work for checking the last two digits not all the digits, so if you can help me out and check it for me and if you have any better ideas on how to tackle this problem, please share.

input = raw_input("Enter 9 Digits: ")
x = 9
while x>0:
    x-=1
    if input[8] > input[7]:
        temp = input[8]
        input[8] == input[7] 
        input[7] == temp
        print input
        break
  • Can you add any error output from your run? – Dagrooms Jul 27 '15 at 13:59
  • A less clever (and less efficient) but shorter program could use `min(x for x in (int(''.join(p)) for p in itertools.permutations(s)) if x > int(s))` - i.e. it generates all permutations of the given number and then picks the smallest number greater than the given number. – Frerich Raabe Jul 27 '15 at 14:32
  • 1
    `input` is a reserved word both in Python2 and Python3. Use something else, for example `user_input` instead. Otherwise you shade the built-in input method. – cezar Jul 27 '15 at 15:05
  • Also see [Algorithm to find next greater permutation of a given string](http://stackoverflow.com/questions/1622532/algorithm-to-find-next-greater-permutation-of-a-given-string). – PM 2Ring Jul 28 '15 at 14:13

3 Answers3

6

Here's a more efficient approach, using the algorithm of the 14th century Indian mathematician Narayana Pandita, which can be found in the Wikipedia article on Permutation. This ancient algorithm is still one of the fastest known ways to generate permutations in order, and it is quite robust, in that it properly handles permutations that contain repeated elements.

The code below includes a simple test() function that generates all permutations of an ordered numeric string.

#! /usr/bin/env python

''' Find the next permutation in lexicographic order after a given permutation

    This algorithm, due to Narayana Pandita, is from
    https://en.wikipedia.org/wiki/Permutation#Generation_in_lexicographic_order

    1. Find the largest index j such that a[j] < a[j + 1]. If no such index exists, 
    the permutation is the last permutation.
    2. Find the largest index k greater than j such that a[j] < a[k].
    3. Swap the value of a[j] with that of a[k].
    4. Reverse the sequence from a[j + 1] up to and including the final element a[n].

    Implemented in Python by PM 2Ring 2015.07.28
'''

import sys

def next_perm(a):
    ''' Advance permutation a to the next one in lexicographic order '''
    n = len(a) - 1
    #1. Find the largest index j such that a[j] < a[j + 1]
    for j in range(n-1, -1, -1):
        if a[j] < a[j + 1]:
            break
    else:
        #This must be the last permutation
        return False

    #2. Find the largest index k greater than j such that a[j] < a[k]
    v = a[j]
    for k in range(n, j, -1):
        if v < a[k]:
            break

    #3. Swap the value of a[j] with that of a[k].
    a[j], a[k] = a[k], a[j]

    #4. Reverse the tail of the sequence
    a[j+1:] = a[j+1:][::-1]

    return True


def test(n):
    ''' Print all permutations of an ordered numeric string (1-based) '''
    a = [str(i) for i in range(1, n+1)]
    i = 0
    while True:
        print('%2d: %s' % (i, ''.join(a)))
        i += 1
        if not next_perm(a):
            break


def main():
    s = sys.argv[1] if len(sys.argv) > 1 else '781623954'
    a = list(s)
    next_perm(a)
    print('%s -> %s' % (s, ''.join(a)))


if __name__ == '__main__':
    #test(4)
    main()
PM 2Ring
  • 54,345
  • 6
  • 82
  • 182
  • FWIW, my answer [here](http://stackoverflow.com/a/37765901/4014959) has a generator version of this permutation algorithm. – PM 2Ring Jan 11 '17 at 08:10
1

I am not convinced that your approach of flipping digits is guaranteed to find the next highest number (at least not without further checks)

Here a simple solution: Simply increment the input number and check if the conditions are met or if no number can be found.

set() can be used to get the set of unique digits in the number.

input_num = '781623954'
next_num = int(input_num) + 1
input_digits = set(input_num)
found = False
while not found:
    next_num += 1
    next_digits = set(str(next_num))
    found = len(next_digits) == 9 and input_digits == next_digits
    if next_num > 987654321:
        break

if found:
    print(next_num)
else:
    print("No number was found.")
b3000
  • 1,547
  • 1
  • 15
  • 27
  • but where in this code that you just gave me does it check if it uses the same exact 9 digits presented by the user, because i want to user to enter a number and then the program would generate the next highest, i don't need it to do that with a previously planned out number – David Wakim Jul 27 '15 at 15:22
  • `set(input) == set(str(next))` does that. It checks if both sets are equal, i.e. contain the same digits. – b3000 Jul 27 '15 at 15:24
  • Thank you So much, actually this helps a lot. Thank you kind man :) – David Wakim Jul 27 '15 at 15:29
  • cezar has already commented on the question about not using `input` as a variable name (but I guess you just copied that from the OP). However, you shouldn't use `next` for the same reason. Also, you could make this algorithm more efficient by computing `set(input)` outside the loop, and not computing `set(str(next))` twice per loop. – PM 2Ring Jul 28 '15 at 12:35
  • Thanks for the suggestions. I edited it accordingly. – b3000 Jul 28 '15 at 13:14
  • Much better! But I think you'll agree that mine's a little more efficient. :) OTOH, yours is easier to understand, and if David's only looking for one permutation the speed difference is immaterial. But FWIW, on the test string in the OP, my code is about 180 times faster than yours (as measured using the `timeit` module). – PM 2Ring Jul 28 '15 at 14:08
0
input[8] == input[7]
input[7] == temp

you probably meant:

input[8] = input[7]
input[7] = temp

didn't you?

Which, as stated in the comments, wouldn't work directly on the string, as it is immutable in Python. So, as a first step, you could make a list of characters from that string:

input = list(input)

and as a last step, get a string back from the modified list:

input = ''.join(input)

BTW, you might want to benefit from Python tuple unpacking which allows you to swap two variables without having to introduce a third one:

input[7], input[8] = input[8], input[7]
Arkanosis
  • 2,229
  • 1
  • 12
  • 18
  • Possibly... but "TypeError: 'str' object does not support item assignment" – PM 2Ring Jul 27 '15 at 14:02
  • well the one = doesnt work for some reason but that was my initial intent – David Wakim Jul 27 '15 at 14:03
  • @DavidWakim: Python strings are immutable, so you can't change their contents like that. And that's why you get the error message I put in my 1st comment. – PM 2Ring Jul 27 '15 at 14:05
  • @PM2Ring: i keep getting that error and i dont know how to fix it, i want to change the values of the indexes, is there an easier or a working way to do so – David Wakim Jul 27 '15 at 14:10
  • @Arkanosis: if i do use your suggestion "input[7], input[8] = input[8], input[7]" then when i state to print the string it print the same exact thing without flipping the indexes – David Wakim Jul 27 '15 at 14:14
  • @DavidWakim: do you convert `input` to a list first, and then back to a string before printing as suggested? – Arkanosis Jul 27 '15 at 14:18
  • no, i leave the input as is, because i dont know how to convert it, maybe converting could make it easy – David Wakim Jul 27 '15 at 15:25
  • Look at what I suggested above: use `input = list(input)` to convert it from string to list (at the beginning), then `input = ''.join(input)` to convert it back to string (at the end). Until you do something like this, you won't be able to modify individual characters. – Arkanosis Jul 27 '15 at 15:27