2

このトピックがよく議論されていることは知っていますが、私のニーズに合った実装が見つからないようです。

次の文字セットがあります。

abcdefgh

すべての可能な順列または組み合わせ (繰り返しなし) を取得したいのですが、限られた (可変の) 文字セットで、文字と数字を入力すると2、結果は次のようになります。

ab ba ac ca ad da ae ea af fa ag ga ah ha
bc cb bd db be eb bf fb bg gb bh hb
cd dc ce ec cf fc cg gc ch hc
de ed df fd dg gd dh hd
ef fe eg ge eh he
fg gf fh hf
gh hg

これでどこに行くのか理解していただければ幸いです。現在、すべての文字の順列を提供する実装がありますが、これらの順列のために限られたスペースを実装する方法について頭を悩ませることはできません。

public function getPermutations($letters) {
    if (strlen($letters) < 2) {
        return array($letters);
    }

    $permutations = array();
    $tail = substr($letters, 1);

    foreach ($this->getPermutations($tail) as $permutation) {
        $length = strlen($permutation);

        for ($i = 0; $i <= $length; $i++) {
            $permutations[] = substr($permutation, 0, $i) . $letters[0] . substr($permutation, $i);
        }
    }

    return $permutations;
}
4

2 に答える 2

4

一度に 1 つの要素のみが必要な場合は、各要素を個別に生成することでメモリを節約できます。

予想される出力のセットでランダムな文字列を生成したい場合は、次のアルゴリズムを使用できます。

Given a set of characters S, and a desired output length K:
  While the output has less than K characters:
    Pick a random number P between 1 and |S|.
    Append the P'th character to the output.
    Remove the P'th character from S.

ここ|S|で、 は S の現在の要素数です。

この一連の選択を実際に整数にエンコードできます。これを行う 1 つの方法は、アルゴリズムを次のように変更することです。

Given a set of characters S, and a desired output length K:
  Let I = 0.
  While the output has less than K characters:
    I = I * (|S| + 1).
    Pick a random number P between 1 and the number of elements in S.
    I = I + P.
    Append the P'th character to the output.
    Remove the P'th character from S.

このアルゴリズムを実行すると、値Iはこの特定の選択シーケンスを一意にエンコードします。基本的に、これを混合基数としてエンコードします。1 つの数字は基数 N を使用し、次の数字は基数 N-1 を使用し、基数 N-K+1 (N は入力の文字数) である最後の数字まで同様に使用します。

当然、これを再度デコードすることもできます。PHP では、次のようになります。

// Returns the total number of $count-length strings generatable from $letters.
function getPermCount($letters, $count)
{
  $result = 1;
  // k characters from a set of n has n!/(n-k)! possible combinations
  for($i = strlen($letters) - $count + 1; $i <= strlen($letters); $i++) {
    $result *= $i;
  }
  return $result;
}

// Decodes $index to a $count-length string from $letters, no repeat chars.
function getPerm($letters, $count, $index)
{
  $result = '';
  for($i = 0; $i < $count; $i++)
  {
    $pos = $index % strlen($letters);
    $result .= $letters[$pos];
    $index = ($index-$pos)/strlen($letters);
    $letters = substr($letters, 0, $pos) . substr($letters, $pos+1);
  }
  return $result;
}

(簡単にするために、この特定のデコード アルゴリズムは、前に説明したエンコード アルゴリズムと正確には対応していませんが$index、一意の結果への特定のマッピングの望ましい特性を維持していることに注意してください。)

このコードを使用するには、次のようにします。

$letters = 'abcd';
echo '2 letters from 4:<br>';
for($i = 0; $i < getPermCount($letters, 2); $i++)
  echo getPerm($letters, 2, $i).'<br>';

echo '<br>3 letters from 4:<br>';
for($i = 0; $i < getPermCount($letters, 3); $i++)
  echo getPerm($letters, 3, $i).'<br>';
?>
于 2012-11-02T11:29:24.417 に答える
2
$strings = get_perm( range('a', 'h'), 4 );

function get_perm( $a, $c, $step = 0, $ch = array(), $result = array() ){
    if( $c == 1 ){ //if we have last symbol in chain
        for( $k = 0; $k < count( $a ); $k++ ){
            if( @in_array( $k, $ch ) ) continue; // if $k exist in array we already have such symbol in string
            $tmp = '';

            foreach( $ch as $c ) $tmp .= $a[$c]; // concat chain of previous symbols
            $result[] = $tmp . $a[$k]; // and adding current + saving to our array to return
        }
    }else{
        for( $i = 0; $i < count( $a ); $i++ ){
            if( @in_array( $i, $ch ) ) continue;
            $ch[$step] = $i; // saving current symbol for 2 things: check if that this symbol don't duplicate later and to know what symbols and in what order need to be saved
            get_perm( $a, $c-1, $step+1, $ch, &$result ); 
            // recursion, 
            // decrementing amount of symbols left to create string, 
            // incrementing step to correctly save array or already used symbols, 
            // $ch - array of already used symbols, 
            // &$result - pointer to result array
        }
    }

    return $result;
}

知らせ

6 つのシンボルを持つ ah = 配列内の 20k の値
4 つのシンボルを持つ az = 配列内の 358799 の値
したがって、10 個のシンボルを持つ az は確実に停止します =) メモリが多すぎます。
大量の値が必要な場合は、出力をファイルまたはデータベースに保存する必要があります。または、メモリ制限をphpに拡張しますが、これが最善の方法かどうかはわかりません.

于 2012-11-01T11:01:39.587 に答える