Как создать i-ю комбинацию / перестановку без итерации - PullRequest
9 голосов
/ 15 июля 2009

Дана любая итерация, например: "ABCDEF"

Рассматривая это почти как систему счисления как таковую:

A В С D Е F А.А. AB переменный ток ОБЪЯВЛЕНИЕ AE AF BA BB До нашей эры .... FF AAA AAB ....

Как мне найти участника i th в этом списке? Эффективно, не подсчитывая все из них. Я хочу найти миллиардный (например) член в этом списке. Я пытаюсь сделать это в Python, и я использую 2.4 (не по выбору), который может иметь значение, потому что у меня нет доступа к itertools.

Хорошо, но не обязательно: можно ли обобщить решение для псевдо- "смешанного радиуса" системы?

--- РЕЗУЛЬТАТЫ ---

# ------ paul -----
def f0(x, alph='ABCDE'):
    result = ''
    ct = len(alph)
    while x>=0:
        result += alph[x%ct]
        x /= ct-1
    return result[::-1]

# ----- Glenn Maynard -----
import math
def idx_to_length_and_value(n, length):
    chars = 1
    while True:
        cnt = pow(length, chars)
        if cnt > n:
            return chars, n

        chars += 1
        n -= cnt

def conv_base(chars, n, values):
    ret = []
    for i in range(0, chars):
        c = values[n % len(values)]
        ret.append(c)
        n /= len(values)

    return reversed(ret)

def f1(i, values = "ABCDEF"):
    chars, n = idx_to_length_and_value(i, len(values))
    return "".join(conv_base(chars, n, values))

# -------- Laurence Gonsalves ------
def f2(i, seq):
    seq = tuple(seq)
    n = len(seq)
    max = n # number of perms with 'digits' digits
    digits = 1
    last_max = 0
    while i >= max:
        last_max = max
        max = n * (max + 1)
        digits += 1
    result = ''
    i -= last_max
    while digits:
        digits -= 1
        result = seq[i % n] + result
        i //= n
    return result

# -------- yairchu -------
def f3(x, alphabet = 'ABCDEF'):
    x += 1 # Make us skip "" as a valid word
    group_size = 1
    num_letters = 0
    while 1: #for num_letters in itertools.count():
        if x < group_size:
            break
        x -= group_size
        group_size *= len(alphabet)
        num_letters +=1
    letters = []
    for i in range(num_letters):
        x, m = divmod(x, len(alphabet))
        letters.append(alphabet[m])
    return ''.join(reversed(letters))

# ----- testing ----
import time
import random
tries = [random.randint(1,1000000000000) for i in range(10000)]
numbs = 'ABCDEF'

time0 = time.time()
s0 = [f1(i, numbs) for i in tries]
print 's0 paul',time.time()-time0, 'sec'
time0 = time.time()
s1 = [f1(i, numbs) for i in tries]
print 's1 Glenn Maynard',time.time()-time0, 'sec'
time0 = time.time()
s2 = [f2(i, numbs) for i in tries]
print 's2 Laurence Gonsalves',time.time()-time0, 'sec'
time0 = time.time()
s3 = [f3(i,numbs) for i in tries]
print 's3 yairchu',time.time()-time0, 'sec'

раз:

s0 paul 0.470999956131 sec
s1 Glenn Maynard 0.472999811172 sec
s2 Laurence Gonsalves 0.259000062943 sec
s3 yairchu 0.325000047684 sec
>>> s0==s1==s2==s3
True

Ответы [ 8 ]

5 голосов
/ 15 июля 2009

Multi-radix решение внизу.

import math
def idx_to_length_and_value(n, length):
    chars = 1
    while True:
        cnt = pow(length, chars)
        if cnt > n:
            return chars, n

        chars += 1
        n -= cnt

def conv_base(chars, n, values):
    ret = []
    for i in range(0, chars):
        c = values[n % len(values)]
        ret.append(c)
        n /= len(values)

    return reversed(ret)

values = "ABCDEF"
for i in range(0, 100):
    chars, n = idx_to_length_and_value(i, len(values))
    print "".join(conv_base(chars, n, values))

import math
def get_max_value_for_digits(digits_list):
    max_vals = []

    for val in digits_list:
        val = len(val)
        if max_vals:
            val *= max_vals[-1]
        max_vals.append(val)
    return max_vals

def idx_to_length_and_value(n, digits_list):
    chars = 1
    max_vals = get_max_value_for_digits(digits_list)

    while True:
        if chars-1 >= len(max_vals):
            raise OverflowError, "number not representable"
        max_val = max_vals[chars-1]
        if n < max_val:
            return chars, n

        chars += 1
        n -= max_val

def conv_base(chars, n, digits_list):
    ret = []
    for i in range(chars-1, -1, -1):
        digits = digits_list[i]
        radix = len(digits)

        c = digits[n % len(digits)]
        ret.append(c)
        n /= radix

    return reversed(ret)

digits_list = ["ABCDEF", "ABC", "AB"]
for i in range(0, 120):
    chars, n = idx_to_length_and_value(i, digits_list)
    print "".join(conv_base(chars, n, digits_list))
5 голосов
/ 15 июля 2009

Очарование в третий раз:

def perm(i, seq):
  seq = tuple(seq)
  n = len(seq)
  max = n # number of perms with 'digits' digits
  digits = 1
  last_max = 0
  while i >= max:
    last_max = max
    max = n * (max + 1)
    digits += 1
  result = ''
  i -= last_max
  while digits:
    digits -= 1
    result = seq[i % n] + result
    i //= n
  return result
3 голосов
/ 15 июля 2009

То, что вы делаете, близко к преобразованию из базы 10 (вашего номера) в базу 6, где ABCDEF - ваши цифры. Разница лишь в том, что «АА» и «А» различаются, что неправильно, если считать «А» нулевой цифрой.

Если вы добавите следующую большую шестерку к своему номеру, а затем выполните базовое преобразование в базу 6, используя эти цифры, и, наконец, удалите первую цифру (которая должна быть буквой «B», то есть «1») , у вас есть результат.

Я просто хочу опубликовать здесь идею, а не реализацию, потому что вопрос пахнет для меня как домашняя работа (я даю преимущество сомнения, это просто мое чувство).

2 голосов
/ 17 июля 2009

Это работает (и это то, на чем я наконец остановился), и подумал, что это стоит опубликовать, потому что это аккуратно. Однако это медленнее, чем большинство ответов. Могу ли я выполнить% и / в той же операции?

def f0(x, alph='ABCDE'):
    result = ''
    ct = len(alph)
    while x>=0:
        result += alph[x%ct]
        x /= ct-1
    return result[::-1]
2 голосов
/ 15 июля 2009

Сначала вычислите длину, суммируя степени шести, пока не превысите свой индекс (или лучше используйте формулу для геометрического ряда).

Вычтите сумму меньших степеней из индекса.

Вычислить представление для базы 6, заполнить начальные нули и отобразить 0 -> A, ..., 5 -> F.

1 голос
/ 15 июля 2009

Так как мы преобразуем из числа Base (10) в число Base (7), избегая при этом всех «0» в выводе, нам нужно будет отрегулировать оригинальное число, поэтому мы делаем пропускать по одному каждый раз, когда результат будет содержать «0».

 1 => A,  or 1  in base [0ABCDEF]
 7 => AA, or 8  in base [0ABCDEF]
13 => BA, or 15 in base [0ABCDEF]
42 => FF, or 48 in base [0ABCDEF]
43 =>AAA, or 50 in base [0ABCDEF]

Вот некоторый Perl-код, который показывает, что я пытаюсь объяснить (извините, не видел, что это запрос Python)

use strict;
use warnings;
my @Symbols=qw/0 A B C D E F/;
my $BaseSize=@Symbols ;
for my $NR ( 1 .. 45) {
   printf ("Convert %3i => %s\n",$NR ,convert($NR));
}

sub convert {
   my ($nr,$res)=@_;
   return $res unless $nr>0;
   $res="" unless defined($res);
   #Adjust to skip '0'
   $nr=$nr + int(($nr-1)/($BaseSize-1));
   return convert(int($nr/$BaseSize),$Symbols[($nr % ($BaseSize))] . $res);
}
1 голос
/ 15 июля 2009
alphabet = 'ABCDEF'

def idx_to_excel_column_name(x):
  x += 1 # Make us skip "" as a valid word
  group_size = 1
  for num_letters in itertools.count():
    if x < group_size:
      break
    x -= group_size
    group_size *= len(alphabet)
  letters = []
  for i in range(num_letters):
    x, m = divmod(x, len(alphabet))
    letters.append(alphabet[m])
  return ''.join(reversed(letters))

def excel_column_name_to_idx(name):
  q = len(alphabet)
  x = 0
  for letter in name:
    x *= q
    x += alphabet.index(letter)
  return x+q**len(name)//(q-1)-1
0 голосов
/ 15 июля 2009

В Perl вы просто конвертируете свои входные данные i из base (10) в base (длина "ABCDEF"), а затем делаете tr/012345/ABCDEF/, что совпадает с y/0-5/A-F/. Конечно, Python имеет аналогичный набор функций.

О, как указывает Яричу комбинации немного отличаются, потому что если бы А представлял 0, то не было бы комбинаций с лидирующим А (хотя он сказал, что это немного по-другому). Кажется, я думал, что проблема более тривиальна, чем она есть. Вы не можете просто транслитерировать разные базовые числа, потому что числа, содержащие эквивалент 0, будут пропущено в последовательности.

Так что я предложил на самом деле только последний шаг из того, что starblue предложил, что, по сути, Laurence Gonsalves реализовал в ftw. О, и в Python нет операции транслитерации (tr// или y//), какой позор.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...