正の整数の配列があるとします。結果の配列の連結が可能な最大数になるように順序を操作したいと思います。たとえば、次のよう[97, 9, 13]
になります99713
。[9,1,95,17,5]
になります9955171
。答えがわかりません。
8 に答える
sorted(x, cmp=lambda a, b: -1 if str(b)+str(a) < str(a)+str(b) else 1)
直感的に、1 桁の数字を逆順に並べ替えると、最大の数字になることがわかります。
>>> ''.join(sorted(['1', '5', '2', '9'], reverse=True))
'9521'
したがって、逆ソートが機能するはずです。入力に複数桁のスニペットがある場合に問題が発生します。ここでも、直観により9
before95
と17
beforeを順序付けることができます1
が、なぜそれが機能するのでしょうか? 繰り返しますが、それらが同じ長さであった場合、それらを並べ替える方法は明らかでした。
95 < 99
96 < 97
14 < 17
秘訣は、短い数値を「拡張」して、長い数値と比較し、辞書式に自動的にソートできるようにすることです。実際に行う必要があるのは、スニペットを最大長を超えて繰り返すことだけです。
9
comparison and95
:代わりに and を比較し、したがって最初999
に来る。9595
999
1
comparison and17
:代わりに and を比較し、したがって最初111
に来る。1717
1717
132
comparison and13
:代わりに and を比較し、したがって最初132132
に来る。1313
132132
23
comparison and2341
:代わりに and を比較し、したがって最初232323
に来る。23412341
2341
これが機能するのは、Python が 2 つのスニペットをどこかで異なるまで比較する必要があるためです。また、2 つのスニペットを比較して最大数を形成するためにどの順序にする必要があるかを判断するときにスキップする必要があるのは、(繰り返し) 一致するプレフィックスです。
2 つのスニペットを比較するときに一致しない最初の数字を確実に見つけることができるようにするには、入力内の最長のスニペット * 2 よりも長くなるまでスニペットを繰り返す必要があります。
key
への引数を使用してこれを行うことができますがsorted()
、最初にスニペットの最大長を決定する必要があります。その長さを使用すると、その最大長よりも長くなるまで、ソート キー内のすべてのスニペットを「パディング」できます。
def largestpossible(snippets):
snippets = [str(s) for s in snippets]
mlen = max(len(s) for s in snippets) * 2 # double the length of the longest snippet
return ''.join(sorted(snippets, reverse=True, key=lambda s: s*(mlen//len(s)+1)))
スニペットをそれ自体でパディングして、長さs*(mlen//len(s)+1)
以上mlen
にします。
これは与える:
>>> combos = {
... '12012011': [1201, 120, 1],
... '87887': [87, 878],
... '99713': [97, 9, 13],
... '9955171': [9, 1, 95, 17, 5],
... '99799713': [97, 9, 13, 979],
... '10100': [100, 10],
... '13213': [13, 132],
... '8788717': [87, 17, 878],
... '93621221': [936, 21, 212],
... '11101110': [1, 1101, 110],
... }
>>> def test(f):
... for k,v in combos.items():
... print '{} -> {} ({})'.format(v, f(v), 'correct' if f(v) == k else 'incorrect, should be {}'.format(k))
...
>>> test(largestpossible)
[97, 9, 13] -> 99713 (correct)
[1, 1101, 110] -> 11101110 (correct)
[936, 21, 212] -> 93621221 (correct)
[13, 132] -> 13213 (correct)
[97, 9, 13, 979] -> 99799713 (correct)
[87, 878] -> 87887 (correct)
[1201, 120, 1] -> 12012011 (correct)
[100, 10] -> 10100 (correct)
[9, 1, 95, 17, 5] -> 9955171 (correct)
[87, 17, 878] -> 8788717 (correct)
このソリューションは、a) 3 行短く、b) Python 3 でも頼りにせずに動作しfunctools.cmp_to_key()
、c) ソリューションをブルートフォースしません (これがitertools.permutations
オプションの機能です)。
ヒント 1: 整数ではなく、文字列を連結します。ヒント 2: itertools.permutations()
.
import itertools
nums = ["9", "97", "13"]
m = max(("".join(p) for p in itertools.permutations(nums)), key = int)
itertools.permutationsをヒントとして使用し、結合関数と連結した後、max関数のkey引数(最大値を決定するために各要素に適用する関数を指示する)を使用できます。
そもそも文字列を扱う方が簡単です。
私はこれに対する力ずくのアプローチが好きではありません。大規模なセットには大量の計算が必要です。
sorted組み込みメソッド用に独自の比較関数を作成できます。この比較関数は、関数に追加した任意のロジックに基づいて、任意のペアの並べ替えパラメーターを返します。
サンプルコード:
def compareInts(a,b):
# create string representations
sa = str(a)
sb = str(b)
# compare character by character, left to right
# up to first inequality
# if you hit the end of one str before the other,
# and all is equal up til then, continue to next step
for i in xrange(min(len(sa), len(sb))):
if sa[i] > sb[i]:
return 1
elif sa[i] < sb[i]:
return -1
# if we got here, they are both identical up to the length of the shorter
# one.
# this means we need to compare the shorter number again to the
# remainder of the longer
# at this point we need to know which is shorter
if len(sa) > len(sb): # sa is longer, so slice it
return compareInts(sa[len(sb):], sb)
elif len(sa) < len(sb): # sb is longer, slice it
return compareInts(sa, sb[len(sa):])
else:
# both are the same length, and therefore equal, return 0
return 0
def NumberFromList(numlist):
return int(''.join('{}'.format(n) for n in numlist))
nums = [97, 9, 13, 979]
sortednums = sorted(nums, cmp = compareInts, reverse = True)
print nums # [97, 9, 13, 979]
print sortednums # [9, 979, 97, 13]
print NumberFromList(sortednums) # 99799713
ええと、ブルートフォースアプローチは常にあります...
from itertools import permutations
lst = [9, 1, 95, 17, 5]
max(int(''.join(str(x) for x in y)) for y in permutations(lst))
=> 9955171
または、これは、質問で指定されているように、整数のリストを受け取り、整数を返す@Zahの回答の適応です。
int(max((''.join(y) for y in permutations(str(x) for x in lst)), key=int))
=> 9955171
あなたはいくつかの巧妙な分類でこれを行うことができます。
2つの弦が同じ長さの場合は、2つのうち大きい方を最初に選択します。簡単。
それらが同じ長さでない場合は、可能な限り最良の組み合わせを短い方の組み合わせに追加した場合の結果を把握してください。短いものに続くものはすべてそれ以下でなければならないので、長いものと同じサイズになるまで短いものをそれ自体に追加することによってこれを決定できます。それらが同じ長さになったら、前と同じように直接比較します。
2番目の比較が等しい場合、短い文字列が長い文字列よりも優れている可能性はないことが証明されています。ペアリングされているものによっては、さらに悪化する可能性があるため、長い方が先に来る必要があります。
def compare(s1, s2):
if len(s1) == len(s2):
return -1 if s1 > s2 else int(s2 > s1)
s1x, s2x = s1, s2
m = max(len(s1), len(s2))
while len(s1x) < m:
s1x = s1x + s1
s1x = s1x[:m]
while len(s2x) < m:
s2x = s2x + s2
s2x = s2x[:m]
return -1 if s1x > s2x or (s1x == s2x and len(s1) > len(s2)) else 1
def solve_puzzle(seq):
return ''.join(sorted([str(x) for x in seq], cmp=compare))
>>> solve_puzzle([9, 1, 95, 17, 5])
'9955171'
>>> solve_puzzle([97, 9, 13])
'99713'
>>> solve_puzzle([936, 21, 212])
'93621221'
>>> solve_puzzle([87, 17, 878])
'8788717'
>>> solve_puzzle([97, 9, 13, 979])
'99799713'
これは、すべての順列を実行するよりもはるかに効率的です。
import itertools
def largestInt(a):
b = list(itertools.permutations(a))
c = []
x = ""
for i in xrange(len(b)):
c.append(x.join(map(str, b[i])))
return max(c)