Kビットセットを含む長さnのすべてのバイナリ文字列を見つけるための最良のアルゴリズムは何ですか?たとえば、n = 4およびk = 3の場合、...
0111
1011
1101
1110
任意のnと任意のkを指定してこれらを生成するための良い方法が必要なので、文字列を使用することをお勧めします。
このメソッドは、正確にN '1'ビットのすべての整数を生成します。
から https://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation
辞書編集的に次のビット順列を計算する
整数で1に設定されたNビットのパターンがあり、辞書編集の意味でN 1ビットの次の置換が必要であるとします。たとえば、Nが3で、ビットパターンが_
00010011
_の場合、次のパターンは_00010101
_、_00010110
_、_00011001
_、_00011010
_、_00011100
_、_00100011
_など。次は、次の順列を計算する高速な方法です。_unsigned int v; // current permutation of bits unsigned int w; // next permutation of bits unsigned int t = v | (v - 1); // t gets v's least significant 0 bits set to 1 // Next set to 1 the most significant bit to change, // set to 0 the least significant ones, and add the necessary 1 bits. w = (t + 1) | (((~t & -~t) - 1) >> (__builtin_ctz(v) + 1));
_
__builtin_ctz(v)
GNU x86 CPU用Cコンパイラ組み込み関数は、末尾のゼロの数を返します。x86用Microsoftコンパイラを使用している場合、組み込み関数は__BitScanForward
_です。どちらもbsf
命令を発行しますが、他のアーキテクチャでも同等の命令が利用できる場合があります。除算演算子ですが、後続ゼロをカウントする必要はありません。_unsigned int t = (v | (v - 1)) + 1; w = t | ((((t & -t) / (v & -v)) >> 1) - 1);
_2009年11月28日にこれを提供してくれたアルゼンチンのDario Sneidermanisに感謝します。
Python
import itertools
def kbits(n, k):
result = []
for bits in itertools.combinations(range(n), k):
s = ['0'] * n
for bit in bits:
s[bit] = '1'
result.append(''.join(s))
return result
print kbits(4, 3)
Output: ['1110', '1101', '1011', '0111']
説明:
基本的に、1ビットの位置を選択する必要があります。 n個の合計ビットの中からkビットを選択するn個のk個の選択方法があります。 itertoolsは、これを実行するNiceモジュールです。 itertools.combinations(range(n)、k)は、[0、1、2 ... n-1]からkビットを選択し、それらのビットインデックスを指定して文字列を構築するだけです。
Pythonを使用していないので、次のitertools.combinationsの擬似コードをご覧ください。
http://docs.python.org/library/itertools.html#itertools.combinations
どの言語でも簡単に実装できる必要があります。
実装を忘れる(「be it done with strings」は明らかにimplementationの問題です!)-algorithmについて考えてください。ピートのために…ちょうどあなたの最初のTAGのように!
探しているのは、Nのセット(セットビットの0〜N-1のインデックス)からのK個のアイテムのすべての組み合わせです。それは、例えば、擬似コードのように、再帰的に表現するのが明らかに最も簡単です:
combinations(K, setN):
if k > length(setN): return "no combinations possible"
if k == 0: return "empty combination"
# combinations INcluding the first item:
return (first-item-of setN) combined combinations(K-1, all-but-first-of setN)
union combinations(K-1, all-but-first-of setN)
つまり、最初のアイテムは存在するか、存在しないかのいずれかです:存在する場合、K-1を残しておきます(テールから別名all-but-firs)、存在しない場合は、まだKを残します。
SMLやHaskellなどのパターンマッチング関数型言語は、この擬似コードを表現するのに最適かもしれません(私の大好きなPythonのような手続き型言語は、itertools.combinations
、それはあなたのためにすべてのハードワークを行い、したがってあなたからそれを隠します!)。
この目的で最もよく知っているのは、Scheme、SML、Haskell、...?上記の擬似コードを翻訳させていただきます。もちろん、Pythonも)などの言語でそれを行うことができますが、この宿題の割り当ての仕組みを理解してもらうことがポイントなので、あまりリッチな機能は使用しません。なので itertools.combinations
が、より明確なプリミティブ(ヘッド、テール、連結など)での再帰(および必要に応じて再帰除去)。しかし、あなたが最もよく知っている擬似コードのような言語を教えてください! (あなたが述べる問題は、「K個のアイテムのすべての組み合わせを範囲外または範囲(N)にする」ことと同等に等しいことを理解していますか?).
このC#メソッドは、すべての組み合わせを作成する列挙子を返します。列挙した組み合わせを作成するとき、スタックスペースのみを使用するため、作成できる組み合わせの数のメモリスペースによって制限されません。
これは私が思いついた最初のバージョンです。スタックスペースの長さは約2700に制限されています。
static IEnumerable<string> BinStrings(int length, int bits) {
if (length == 1) {
yield return bits.ToString();
} else {
if (length > bits) {
foreach (string s in BinStrings(length - 1, bits)) {
yield return "0" + s;
}
}
if (bits > 0) {
foreach (string s in BinStrings(length - 1, bits - 1)) {
yield return "1" + s;
}
}
}
}
これは2番目のバージョンで、最初の文字を分割するのではなくバイナリ分割を使用するため、スタックをより効率的に使用します。それは、各反復で作成する文字列のメモリ空間によってのみ制限され、10000000の長さまでテストしました。
static IEnumerable<string> BinStrings(int length, int bits) {
if (length == 1) {
yield return bits.ToString();
} else {
int first = length / 2;
int last = length - first;
int low = Math.Max(0, bits - last);
int high = Math.Min(bits, first);
for (int i = low; i <= high; i++) {
foreach (string f in BinStrings(first, i)) {
foreach (string l in BinStrings(last, bits - i)) {
yield return f + l;
}
}
}
}
}
この問題の多くの標準的な解決策の1つの問題は、文字列のセット全体が生成され、それらが繰り返されることです。これにより、スタックが使い果たされる可能性があります。最小セット以外の場合、すぐに扱いにくくなります。さらに、多くの場合、部分的なサンプリングのみが必要ですが、標準的な(再帰的な)ソリューションは一般に、一方向に大きく偏っている部分に問題を切り分けます(たとえば、ゼロの開始ビットを持つすべてのソリューションを検討し、開始ビットが1つのソリューション)。
多くの場合、関数にビット文字列(要素の選択を指定する)を渡し、最小限の変更を行うような方法で次のビット文字列を返すようにする方が望ましいでしょう(これは灰色として知られています)コード)およびすべての要素の表現を持っています。
Donald Knuthは、Fascicle 3Aのセクション7.2.1.3:すべての組み合わせの生成で、このためのアルゴリズムのホスト全体をカバーしています。
http://answers.yahoo.com/question/index?qid=20081208224633AA0gdMl にあるnからk個の要素を選択するすべての方法で、反復グレイコードアルゴリズムに取り組むアプローチがあります。 PHPページの下部にあるコメント(クリックして展開)にリストされているコード)。
動作するはずの1つのアルゴリズム:
generate-strings(prefix, len, numBits) -> String:
if (len == 0):
print prefix
return
if (len == numBits):
print prefix + (len x "1")
generate-strings(prefix + "0", len-1, numBits)
generate-strings(prefix + "1", len-1, numBits)
幸運を!
より一般的な方法では、以下の関数は、N choose K問題のすべての可能なインデックスの組み合わせを提供します。これは、文字列などに適用できます。
def generate_index_combinations(n, k):
possible_combinations = []
def walk(current_index, indexes_so_far=None):
indexes_so_far = indexes_so_far or []
if len(indexes_so_far) == k:
indexes_so_far = Tuple(indexes_so_far)
possible_combinations.append(indexes_so_far)
return
if current_index == n:
return
walk(current_index + 1, indexes_so_far + [current_index])
walk(current_index + 1, indexes_so_far)
if k == 0:
return []
walk(0)
return possible_combinations
考えられる1.5ライナーの1つ:
$ python -c 'import itertools; \
print set([ n for n in itertools.permutations("0111", 4)])'
set([('1', '1', '1', '0'), ('0', '1', '1', '1'), ..., ('1', '0', '1', '1')])
..ここで、k
は、1
内の"0111"
sの数です。
Itertoolsモジュールは、そのメソッドに相当するものを説明しています。 permutation method に相当するものを参照してください。
this 質問(設定ビット数の昇順ですべてのサブマスクを反復する必要がある)については、これの複製としてマークされています。
すべてのサブマスクを単純に繰り返してベクトルに追加し、設定されたビット数に従って並べ替えることができます。
vector<int> v;
for(ll i=mask;i>0;i=(i-1)&mask)
v.Push_back(i);
auto cmp = [](const auto &a, const auto &b){
return __builtin_popcountll(a) < __builtin_popcountll(b);
}
v.sort(v.begin(), v.end(), cmp);
別の方法は、すべてのサブマスクをN回反復し、i番目の反復で設定ビット数がiに等しい場合、ベクトルに数値を追加することです。
どちらの方法にもO(n * 2 ^ n)の複雑さがあります
再帰を試みます。
それらのkが1であるn桁があります。これを表示するもう1つの方法は、k + 1スロットのシーケンスで、n-k 0がそれらの間に分散されています。つまり、(0の実行後に1が続く)k回、その後に別の0の実行が続きます。これらの実行はいずれも長さゼロにすることができますが、全長はn-kである必要があります。
これをk + 1整数の配列として表します。再帰の下部で文字列に変換します。
再帰呼び出しの前に配列の1つの要素をインクリメントし、その後k + 1回デクリメントするメソッドであるdepth n-kを再帰的に呼び出します。
N-kの深さで、文字列を出力します。
int[] run = new int[k+1];
void recur(int depth) {
if(depth == 0){
output();
return;
}
for(int i = 0; i < k + 1; ++i){
++run[i];
recur(depth - 1);
--run[i];
}
public static void main(string[] arrrgghhs) {
recur(n - k);
}
Javaを作成してからしばらく経ちましたので、このコードにはおそらくいくつかのエラーがありますが、アイデアは機能するはずです。
python
のitertools.combinations
k
のすべての選択肢を生成し、n
の選択肢を生成し、reduce
を使用してそれらの選択肢をバイナリ配列にマッピングできます。
from itertools import combinations
from functools import reduce # not necessary in python 2.x
def k_bits_on(k,n):
one_at = lambda v,i:v[:i]+[1]+v[i+1:]
return [Tuple(reduce(one_at,c,[0]*n)) for c in combinations(range(n),k)]
使用例:
In [4]: k_bits_on(2,5)
Out[4]:
[(0, 0, 0, 1, 1),
(0, 0, 1, 0, 1),
(0, 0, 1, 1, 0),
(0, 1, 0, 0, 1),
(0, 1, 0, 1, 0),
(0, 1, 1, 0, 0),
(1, 0, 0, 0, 1),
(1, 0, 0, 1, 0),
(1, 0, 1, 0, 0),
(1, 1, 0, 0, 0)]
文字列はintの配列よりも高速ですか?文字列の前にあるすべてのソリューションは、おそらく各反復で文字列のコピーになります。
したがって、おそらく最も効率的な方法は、追加するintまたはcharの配列です。 Javaは、効率的な成長可能なコンテナを持っていますか?文字列よりも高速であれば、それを使用してください。しかし、その後、ビットを反復処理してビットをマスクし、マスクを次のビット位置にビットシフトするため、JITコンパイラーがその頃得意でない限り、おそらく低速になります。
元の質問に対するコメントとしてこれを投稿しますが、私のカルマは十分に高くありません。ごめんなさい。