Using permutations to find the next biggest number

杀马特。学长 韩版系。学妹 提交于 2020-12-15 06:03:14

问题


Create a function that takes a positive integer and returns the next bigger number that can be formed by rearranging its digits.

To accomplish this I used the following code:

Logic used:

  1. Write a script do all the permutations of the number e.g. 123 -> 132,321,231,...
  2. select the largest number from the possible combinations
  3. if that number is bigger than original number provided it returns that number - if not it returns -1.

Code Block:

import itertools
def next_bigger(n):
    # [int(x) for x in str(n)] ex: convert 123 to array [1,2,3]
    # list(set(itertools.permutations... gives you a list of permutation e.g. [[1,3,2],[3,2,1],[2,3,1],...]
    # [int(''.join(map(str,x))) for x in ...  converts each permuted list into a number e.g. convert [2,3,1] -> 231
    # for num in sorted(...if num>n: --> if 321 is bigger than 123, return it
    
     for num in sorted([int(''.join(map(str,x))) for x in list(set(itertools.permutations([int(x) for x in str(n)])))]):
        if num>n:
            return num
        else:
            return -1

Expected Output:

nextBigger(num: 12)   // returns 21
nextBigger(num: 513)  // returns 531
nextBigger(num: 2017) // returns 2071
nextBigger(num: 9)   // returns nil
nextBigger(num: 111) // returns nil
nextBigger(num: 531) // returns nil

Actual Output: Test results fail

Solution Attempt#2:

import itertools
def next_bigger(n):
    # [int(x) for x in str(n)] ex: convert 123 to array [1,2,3]
    # list(set(itertools.permutations... gives you a list of permutation e.g. [[1,3,2],[3,2,1],[2,3,1],...]
    # [int(''.join(map(str,x))) for x in ...  converts each permuted list into a number e.g. convert [2,3,1] -> 231
    # for num in sorted(...if num>n: --> if 321 is bigger than 123, return it
    
    a = []
    results = 0
    for num in sorted([int(''.join(map(str,x))) for x in list(set(itertools.permutations([int(x) for x in str(n)])))]):
        if num>n:
            a.append(num)
    results = a[0]
            
    return results

Expected output: Passes all tests cases except for long numbers

What's wrong with my code?


回答1:


The title says 'using permutations', but you don't need to use permutations for this.

The order of the tuples itertools.permutations emitted is defined; but unfortunately it will not always produce digits that when combined are in increasing order. Also calculating all permutations will take an extremely long time if the number of digits is very large.

To rephrase the question: for any given N, what is the smallest number (N2) such that:

  1. 0 <= N < N2, and
  2. The number of times each digit appears in N and N2 are the same

(if N2 doesn't exist, return None)

We could start from N and do N+=1 until condition 2 is satisfied, or until condition 2 can no longer be satisfied because the total number of digits in N increased. (An int in Python 3 has no defined limit for how big it can get, so we would not get an overflow.) This is still (on average) extremely slow if N is very large, however it will in theory eventually return.

Here is how I would solve this:

def main(N):
    n=list(map(int,str(N)))
    for i_r in reversed(range(len(n))):
        for i_l in reversed(range(i_r)):
            if n[i_l]<n[i_r]:
                t_l=n[i_l]
                t_r=n[i_r]
                n[i_l]=t_r
                n[i_r]=t_l
                return int(''.join(map(str,n)))
    return None

First convert N into a list of digits (while preserving the order). This will make N easier to work on.

Make 2 markers i_l and i_r. i_l and i_r will always be on the left and right respectively. They will start on the least significant (i.e. right) side of N.

They will go through N like this for example:

4321    4321    4321    4321    4321    4321
  lr     l r    l  r     lr     l r     lr  

When i_l < i_r, swap those digits to make a bigger number, which satisfies condition 1. Since i_l and i_r start from the least significant side of N, that new number is the smallest number that satisfies condition 2. If that number doesn't exist, the function returns None.

(The return None is actually unnecessary, as functions by default return None, however explicitly stating it makes it more clear that this is the intended behavior.)

With this solution, even if N is 10000 digits, the function will return in a reasonable amount of time.




回答2:


You need the next biggest number, but currently you pick any permutation-number greater than the input-number. You should be the picking the minimum among such numbers

Also notice, for example for input 9 or 111; there are no other permutations; so you might just want to return the next biggest-permutation or None.

You might consider modifying to this:

import itertools
def next_bigger(n):
    # [int(x) for x in str(n)] ex: convert 123 to array [1,2,3]
    # list(set(itertools.permutations... gives you a list of permutation e.g. [[1,3,2],[3,2,1],[2,3,1],...]
    # [int(''.join(map(str,x))) for x in ...  converts each permuted list into a number e.g. convert [2,3,1] -> 231
    # for num in sorted(...if num>n: --> if 321 is bigger than 123, return it
    
     minval = -1
     for num in sorted([int(''.join(map(str,x))) for x in list(set(itertools.permutations([int(x) for x in str(n)])))]):
        if num>n:
            if minval == -1 or minval > num:
                minval = n
     return minval

Appropriate test-cases:

nextBigger(num: 1176) //should return 1617


来源:https://stackoverflow.com/questions/64886787/using-permutations-to-find-the-next-biggest-number

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!