私はこの質問をRedditで見ましたが、明確な解決策は提示されていなかったので、ここで質問するのは完璧な質問だと思いました。これはインタビューの質問についてのスレッドでした:
サイズmのint配列を取り、配列が数値n ... n + m-1で構成されている場合は(True/False)を返すメソッドを記述します。その範囲内のすべての数値とその範囲内の数値のみです。配列のソートは保証されていません。 (たとえば、{2,3,4}はtrueを返します。{1,3,1}はfalseを返し、{1,2,4}はfalseを返します。
私がこの問題を抱えていた問題は、私のインタビュアーが、一定量の配列を使用して配列の1つのパスでそれを行うことができると主張するまで、最適化(より高速なO(n)、より少ないメモリなど)を私に求め続けたことです。メモリ。その1つを考え出したことはありません。
ソリューションとともに、配列に一意のアイテムが含まれていると想定しているかどうかを示してください。また、ソリューションがシーケンスが1から始まると想定しているかどうかも示します(2、3、4になるケースを許可するために質問を少し変更しました...)
編集:重複を処理する時間線形および空間定数アルゴリズムは存在しないという意見になりました。誰でもこれを確認できますか?
重複の問題は、O(n) time、O(1) space)で配列に重複が含まれているかどうかを確認するテストに要約されます。これが可能であれば単純に最初にテストし、重複がない場合は、アルゴリズムを実行してください。したがって、O(n) time O(1)スペース?
a[i] % a.length
の代わりに a[i]
問題を減らして、数値を持っているかどうかを判断する必要があります0
〜a.length - 1
。
この観察を当然のことと考え、配列に[0、m)が含まれているかどうかを確認します。
正しい位置にない最初のノードを見つけます。
0 1 2 3 7 5 6 8 4 ; the original dataset (after the renaming we discussed)
^
`---this is position 4 and the 7 shouldn't be here
その数をそれがどこに入れ替わるかすべきです。つまり、7
とともに 8
:
0 1 2 3 8 5 6 7 4 ;
| `--------- 7 is in the right place.
`--------------- this is now the 'current' position
これを繰り返します。私たちの現在の立場をもう一度見てみましょう:
「これはここの正しい番号ですか?」
このルールを再びたどると、次のようになります。
0 1 2 3 4 5 6 7 8 ; 4 and 8 were just swapped
これにより、リストが左から右に徐々に正しく作成され、各数値は最大で1回移動されるため、これはO(n)です。
重複がある場合は、リスト内の番号backwards
を入れ替えようとする試みがあるとすぐにわかります。
他のソリューションがすべての値の合計を使用するのはなぜですか? O(n)アイテムを1つの数値に加算すると、技術的にはO(1)スペース以上を使用しているので、これは危険だと思います。
より簡単な方法:
ステップ1、重複がないかどうかを確認します。 O(1) spaceでこれが可能かどうかはわかりません。とにかく、重複がある場合はfalseを返します。
ステップ2、リストを反復処理し、lowestおよびhighest項目を追跡します。
ステップ3、(最高-最低)はmに等しいですか?その場合は、trueを返します。
ワンパスアルゴリズムでは、Omega(n)ビットのストレージが必要です。
逆に、o(n)ビットを使用するワンパスアルゴリズムが存在することを想定します。これは、1つのパスしか作成しないため、最初のn/2値をo(n)space。C(n、n/2)= 2 ^ Theta(n)が存在するため、S = {1、...、n}からn/2値の可能なセットn/2値の2つの異なるセットAとBが存在するため、メモリの状態は両方の後で同じになります。A '= S\AがAを補完する「正しい」値のセットである場合、アルゴリズムはおそらく応答できません。入力に対して正しく
A A '-はい
B A '-いいえ
最初のケースと2番目のケースを区別できないためです。
Q.E.D.
boolean determineContinuousArray(int *arr, int len)
{
// Suppose the array is like below:
//int arr[10] = {7,11,14,9,8,100,12,5,13,6};
//int len = sizeof(arr)/sizeof(int);
int n = arr[0];
int *result = new int[len];
for(int i=0; i< len; i++)
result[i] = -1;
for (int i=0; i < len; i++)
{
int cur = arr[i];
int hold ;
if ( arr[i] < n){
n = arr[i];
}
while(true){
if ( cur - n >= len){
cout << "array index out of range: meaning this is not a valid array" << endl;
return false;
}
else if ( result[cur - n] != cur){
hold = result[cur - n];
result[cur - n] = cur;
if (hold == -1) break;
cur = hold;
}else{
cout << "found duplicate number " << cur << endl;
return false;
}
}
}
cout << "this is a valid array" << endl;
for(int j=0 ; j< len; j++)
cout << result[j] << "," ;
cout << endl;
return true;
}
#include<stdio.h>
#define swapxor(a,i,j) a[i]^=a[j];a[j]^=a[i];a[i]^=a[j];
int check_ntom(int a[], int n, int m) {
int i = 0, j = 0;
for(i = 0; i < m; i++) {
if(a[i] < n || a[i] >= n+m) return 0; //invalid entry
j = a[i] - n;
while(j != i) {
if(a[i]==a[j]) return -1; //bucket already occupied. Dupe.
swapxor(a, i, j); //faster bitwise swap
j = a[i] - n;
if(a[i]>=n+m) return 0; //[NEW] invalid entry
}
}
return 200; //OK
}
int main() {
int n=5, m=5;
int a[] = {6, 5, 7, 9, 8};
int r = check_ntom(a, n, m);
printf("%d", r);
return 0;
}
編集:不正なメモリアクセスを排除するためにコードに変更を加えました。
しばらく前に、電話会社で働いていた誰かから非常に賢い並べ替えアルゴリズムについて聞きました。膨大な数の電話番号を分類する必要がありました。一連のさまざまな並べ替え戦略を実行した後、最終的に非常にエレガントなソリューションを見つけました。ビット配列を作成し、ビット配列へのオフセットを電話番号として処理しただけです。次に、1回のパスでデータベースをスイープし、各番号のビットを1に変更しました。その後、ビット配列を1回スイープし、ビットがハイに設定されているエントリの電話番号を吐き出しました。
これらの線に沿って、重複を探すためのメタデータ構造として配列自体のデータを使用できると思います。最悪の場合、別の配列を使用することもできますが、少し入れ替えてもかまわない場合は、入力配列を使用できると思います。
とりあえずnパラメータは省略します。混乱を招くb/cです。インデックスオフセットを追加するのは簡単です。
考慮してください:
for i = 0 to m
if (a[a[i]]==a[i]) return false; // we have a duplicate
while (a[a[i]] > a[i]) swapArrayIndexes(a[i], i)
sum = sum + a[i]
next
if sum = (n+m-1)*m return true else return false
これはO(n)-おそらくO(n Log n)に近いです-ではありませんが、一定のスペースを提供し、問題に対して異なる攻撃ベクトルを提供する可能性があります。
O(n)が必要な場合は、バイトの配列と一部のビット操作を使用すると、重複チェックに追加のn/32バイトのメモリが使用されます(もちろん32ビットの整数を想定しています)。
編集:上記のアルゴリズムは、ループの内側に合計チェックを追加することでさらに改善できます。
if sum > (n+m-1)*m return false
そうすれば、すぐに失敗します。
私が間違っている場合は投票してください。重複があるかどうか、または分散を使用していないかどうかは判断できます。事前に平均を知っているため(n +(m-1)/ 2またはそのようなもの)、数値と差の2乗を合計して平均が式(mn + m(m-1)/2)であり、分散は(0 + 1 + 4 + ... +(m-1)^ 2)/ mです。分散が一致しない場合は、重複しています。
編集: 要素の半分は平均より小さく、残りの半分はより大きいため、分散は(0 + 1 + 4 + ... + [(m-1)/ 2] ^ 2)* 2/mであると想定されます平均。
重複がある場合、別の重複が平均の変化を完全に相殺しても、上記の方程式の項は正しいシーケンスとは異なります。したがって、関数は、合計と分散の両方が、事前に計算できる望ましい値と一致する場合にのみtrueを返します。
あなたが配列の長さだけを知っていて、配列を変更することが許可されていると仮定すると、O(1) space and O(n)時間。
プロセスには2つの簡単なステップがあります。 1.配列を「モジュロソート」します。 [5,3,2,4] => [4,5,2,3](O(2n))2.各値の近傍がそれ自身より1つ大きいことを確認します(モジュロ)(O(n))
全員が、アレイを最大3回通過する必要があると言いました。
モジュロソートは「トリッキー」な部分ですが、目的は簡単です。配列の各値を取得し、独自のアドレス(モジュロ長)に格納します。これには、配列を1回パスする必要があり、各位置をループして、正しい位置にスワップし、宛先で値を移動することにより、その値を「明示」します。削除したばかりの値と一致する値に移動した場合、重複があり、早期に終了する可能性があります。最悪の場合、それはO(2n)です。
このチェックは、配列を1回パスして、次に大きい隣の値を調べます。常にオン)。
組み合わせたアルゴリズムはO(n)+ O(2n)= O(3n) = O(n)
私のソリューションの疑似コード:
foreach(values []) while(values [i] not i congruent to i) to-bebicted = values [i] evict(values [i])//「適切な」場所にスワップします if(values [i]%length == to-be-beiced%length) return false; //その数を排除したときに「重複」が到着しました end while end foreach foreach(values []) if((values [i] + 1)%length!= values [i + 1]%length) falseを返します foreachを終了します
以下にJavaコードの概念実証コード)を含めました。見栄えは良くありませんが、これは私が作成したすべてのユニットテストに合格しています。これらはポーカーに対応しているため、「StraightArray」と呼んでいますストレートの手(スートを無視した連続したシーケンス)。
public class StraightArray {
static int evict(int[] a, int i) {
int t = a[i];
a[i] = a[t%a.length];
a[t%a.length] = t;
return t;
}
static boolean isStraight(int[] values) {
for(int i = 0; i < values.length; i++) {
while(values[i]%values.length != i) {
int evicted = evict(values, i);
if(evicted%values.length == values[i]%values.length) {
return false;
}
}
}
for(int i = 0; i < values.length-1; i++) {
int n = (values[i]%values.length)+1;
int m = values[(i+1)]%values.length;
if(n != m) {
return false;
}
}
return true;
}
}
これは、Hazzenによって提案された疑似コードに加えて、私自身のアイデアの一部を使用しています。これは負の数でも機能し、平方和の要素を必要としません。
function testArray($nums, $n, $m) {
// check the sum. PHP offers this array_sum() method, but it's
// trivial to write your own. O(n) here.
if (array_sum($nums) != ($m * ($m + 2 * $n - 1) / 2)) {
return false; // checksum failed.
}
for ($i = 0; $i < $m; ++$i) {
// check if the number is in the proper range
if ($nums[$i] < $n || $nums[$i] >= $n + $m) {
return false; // value out of range.
}
while (($shouldBe = $nums[$i] - $n) != $i) {
if ($nums[$shouldBe] == $nums[$i]) {
return false; // duplicate
}
$temp = $nums[$i];
$nums[$i] = $nums[$shouldBe];
$nums[$shouldBe] = $temp;
}
}
return true; // huzzah!
}
var_dump(testArray(array(1, 2, 3, 4, 5), 1, 5)); // true
var_dump(testArray(array(5, 4, 3, 2, 1), 1, 5)); // true
var_dump(testArray(array(6, 4, 3, 2, 0), 1, 5)); // false - out of range
var_dump(testArray(array(5, 5, 3, 2, 1), 1, 5)); // false - checksum fail
var_dump(testArray(array(5, 4, 3, 2, 5), 1, 5)); // false - dupe
var_dump(testArray(array(-2, -1, 0, 1, 2), -2, 5)); // true
(コメントとして投稿することはできません)
@popopome
a = {0, 2, 7, 5,}
の場合はtrue
を返します(a
は[0, 4)
の範囲の順列であることを意味します)が、この場合はfalse
を返す必要があります( a
は明らかに[0, 4)
の順列ではありません)。
別のカウンターの例:{0, 0, 1, 3, 5, 6, 6}
-すべての値は範囲内ですが、重複があります。
Popopomeのアイデア(またはテスト)を誤って実装する可能性があるため、以下にコードを示します。
bool isperm_popopome(int m; int a[m], int m, int n)
{
/** O(m) in time (single pass), O(1) in space,
no restrictions on n,
no overflow,
a[] may be readonly
*/
int even_xor = 0;
int odd_xor = 0;
for (int i = 0; i < m; ++i)
{
if (a[i] % 2 == 0) // is even
even_xor ^= a[i];
else
odd_xor ^= a[i];
const int b = i + n;
if (b % 2 == 0) // is even
even_xor ^= b;
else
odd_xor ^= b;
}
return (even_xor == 0) && (odd_xor == 0);
}
したがって、入力配列の変更を必要とせず、一定のスペースをとるO(n ^ 2)を取るアルゴリズムがあります。
まず、n
とm
を知っていると仮定します。これは線形操作であるため、複雑さが増すことはありません。次に、n
に等しい1つの要素とn+m-1
に等しい1つの要素が存在し、残りはすべて[n, n+m)
にあると想定します。それが与えられれば、問題を[0, m)
の要素を持つ配列に減らすことができます。
これで、要素が配列のサイズによって制限されることがわかったので、各要素を別の要素への単一のリンクを持つノードとして扱うことができます。つまり、配列は有向グラフを表します。この有向グラフでは、重複する要素がない場合、すべてのノードがサイクルに属しています。つまり、ノードはそれ自体からm
以下のステップで到達可能です。重複する要素がある場合は、それ自体からまったく到達できないノードが1つ存在します。
したがって、これを検出するには、配列全体を最初から最後まで調べ、各要素が<=m
ステップで自分自身に戻るかどうかを判断します。 <=m
ステップで到達できない要素がある場合、重複があり、falseを返す可能性があります。それ以外の場合、すべての要素へのアクセスを終了すると、trueを返すことができます。
for (int start_index= 0; start_index<m; ++start_index)
{
int steps= 1;
int current_element_index= arr[start_index];
while (steps<m+1 && current_element_index!=start_index)
{
current_element_index= arr[current_element_index];
++steps;
}
if (steps>m)
{
return false;
}
}
return true;
追加情報を保存することで、これを最適化できます。
sum_of_steps
と呼びます。m-sum_of_steps
ノードのみがアウトになります。開始要素に戻らず、開始要素の前の要素にアクセスしない場合、重複する要素を含むループが見つかり、false
を返す可能性があります。これはまだO(n ^ 2)です。 {1, 2, 3, 0, 5, 6, 7, 4}
ですが、少し高速です。
note:このコメントは質問の元のテキストに基づいています(それ以降修正されています)
上記のように正確に質問が出され(そして単なるタイプミスではない)、サイズnの配列の場合、関数は(True/False )配列が1 ... n + 1の数字で構成されている場合、
...その場合、すべての数値1 ... n + 1を持つ配列はnではなくサイズn + 1になるため、答えは常にfalseになります。したがって、質問はO(1)で回答できます。 :)
連続する配列[n、n + 1、...、n + m-1]は、モジュロ演算子を使用して「ベース」間隔[0、1、...、m]にマッピングできます。区間内の各iについて、基本区間にはちょうど1つのi%mがあり、その逆も同様です。
隣接する配列も、そのサイズに等しい「スパン」m(最大-最小+ 1)を持っています。
これらのファクトを使用して、最初にすべてのfalseを含む同じサイズの「遭遇した」ブール配列を作成し、入力配列にアクセスしているときに、関連する「遭遇した」要素をtrueに設定できます。
このアルゴリズムはO(n)空間内、O(n)時間内であり、重複をチェックします。
def contiguous( values )
#initialization
encountered = Array.new( values.size, false )
min, max = nil, nil
visited = 0
values.each do |v|
index = v % encountered.size
if( encountered[ index ] )
return "duplicates";
end
encountered[ index ] = true
min = v if min == nil or v < min
max = v if max == nil or v > max
visited += 1
end
if ( max - min + 1 != values.size ) or visited != values.size
return "hole"
else
return "contiguous"
end
end
tests = [
[ false, [ 2,4,5,6 ] ],
[ false, [ 10,11,13,14 ] ] ,
[ true , [ 20,21,22,23 ] ] ,
[ true , [ 19,20,21,22,23 ] ] ,
[ true , [ 20,21,22,23,24 ] ] ,
[ false, [ 20,21,22,23,24+5 ] ] ,
[ false, [ 2,2,3,4,5 ] ]
]
tests.each do |t|
result = contiguous( t[1] )
if( t[0] != ( result == "contiguous" ) )
puts "Failed Test : " + t[1].to_s + " returned " + result
end
end
配列にはN個の数値が含まれており、2つの数値の合計が特定の数値Kになるかどうかを判断する必要があります。たとえば、入力が8,4、1,6で、Kが10の場合、答えはイエスです(4および6 )。数値は2回使用できます。以下をせよ。 a。この問題を解決するには、O(N2)アルゴリズムを指定します。b。この問題を解決するには、O(N log N)アルゴリズムを指定します。(ヒント:最初にアイテムを並べ替えます。実行後、線形時間で問題を解決できます。)c。両方のソリューションをコーディングし、アルゴリズムの実行時間を比較します。
他のソリューションがすべての値の合計を使用するのはなぜですか? O(n)アイテムを1つの数値に加算すると、技術的にはO(1)スペース以上を使用しているので、これは危険だと思います。
O(1)は、nの数によって変化しない定数空間を示します。定数であれば1変数でも2変数でもかまいません。なぜO(1) space)を超えていると言うのですか?一時変数に累積してn個の数値の合計を計算する場合、とにかく1つの変数を使用することになります。
システムではまだコメントを書き込むことができないため、回答にコメントする。
更新(コメントへの返信):この回答では、O(1) space "space"または "time"が省略されている場合は常にspaceを意味します。引用されたテキストは、以前の回答の一部です。これは返信です。
数値の合計を知りたい場合[n ... n + m - 1]
この方程式を使用してください。
var sum = m * (m + 2 * n - 1) / 2;
これは、nが10進数であっても、正または負の任意の数値で機能します。
(疑似コードの誤解を避けるため)
カウンターの例:{1, 1, 2, 4, 6, 7, 7}
。
int pow_minus_one(int power)
{
return (power % 2 == 0) ? 1 : -1;
}
int ceil_half(int n)
{
return n / 2 + (n % 2);
}
bool isperm_b3_3(int m; int a[m], int m, int n)
{
/**
O(m) in time (single pass), O(1) in space,
doesn't use n
possible overflow in sum
a[] may be readonly
*/
int altsum = 0;
int mina = INT_MAX;
int maxa = INT_MIN;
for (int i = 0; i < m; ++i)
{
const int v = a[i] - n + 1; // [n, n+m-1] -> [1, m] to deal with n=0
if (mina > v)
mina = v;
if (maxa < v)
maxa = v;
altsum += pow_minus_one(v) * v;
}
return ((maxa-mina == m-1)
and ((pow_minus_one(mina + m-1) * ceil_half(mina + m-1)
- pow_minus_one(mina-1) * ceil_half(mina-1)) == altsum));
}
Pythonの場合:
def ispermutation(iterable, m, n):
"""Whether iterable and the range [n, n+m) have the same elements.
pre-condition: there are no duplicates in the iterable
"""
for i, elem in enumerate(iterable):
if not n <= elem < n+m:
return False
return i == m-1
print(ispermutation([1, 42], 2, 1) == False)
print(ispermutation(range(10), 10, 0) == True)
print(ispermutation((2, 1, 3), 3, 1) == True)
print(ispermutation((2, 1, 3), 3, 0) == False)
print(ispermutation((2, 1, 3), 4, 1) == False)
print(ispermutation((2, 1, 3), 2, 1) == False)
これは、O(m)時間であり、O(1)空間である。notは、重複を考慮に入れる。
代替ソリューション:
def ispermutation(iterable, m, n):
"""Same as above.
pre-condition: assert(len(list(iterable)) == m)
"""
return all(n <= elem < n+m for elem in iterable)
ciphwnはそれを正しく持っています。それはすべて統計に関係しています。質問されるのは、統計的に言えば、数列が離散的な均一分布を形成するかどうかです。離散均一分布は、可能な値の有限セットのすべての値が等しく可能性がある場所です。幸い、離散セットが均一かどうかを判断するためのいくつかの有用な式があります。まず、セットの平均を決定するには、(a..b)は(a + b)/ 2で、分散は(n.n-1)/ 12です。次に、特定のセットの分散を決定します。
variance = sum [i=1..n] (f(i)-mean).(f(i)-mean)/n
そして、予想される分散と比較します。これには、平均を決定するためとデータの分散を計算するための2回のデータパスが必要です。
参照:
おっとっと!私は重複した質問に巻き込まれ、すでに同じ解決策をここで見ませんでした。そして、やっとオリジナルなことができたと思いました!これは私が少し喜んだときの歴史的アーカイブです:
ええと、このアルゴリズムがすべての条件を満たすかどうかは確実ではありません。実際、私が試したいくつかのテストケースを超えて機能することを検証していません。私のアルゴリズムに問題があるとしても、うまくいけば私のアプローチがいくつかの解決策を引き起こすでしょう。
このアルゴリズムは、私の知る限り、一定のメモリで動作し、アレイを3回スキャンします。おそらく、追加のボーナスは、元の問題の一部ではなかった場合、整数の全範囲で機能することです。
私は疑似コードの人ではないので、コードは単に単語よりも意味があると思う。ここに私がPHPで書いた実装があります。コメントに注意してください。
function is_permutation($ints) {
/* Gather some meta-data. These scans can
be done simultaneously */
$lowest = min($ints);
$length = count($ints);
$max_index = $length - 1;
$sort_run_count = 0;
/* I do not have any proof that running this sort twice
will always completely sort the array (of course only
intentionally happening if the array is a permutation) */
while ($sort_run_count < 2) {
for ($i = 0; $i < $length; ++$i) {
$dest_index = $ints[$i] - $lowest;
if ($i == $dest_index) {
continue;
}
if ($dest_index > $max_index) {
return false;
}
if ($ints[$i] == $ints[$dest_index]) {
return false;
}
$temp = $ints[$dest_index];
$ints[$dest_index] = $ints[$i];
$ints[$i] = $temp;
}
++$sort_run_count;
}
return true;
}
(テストを容易にするため)
反例(Cバージョンの場合):{8、33、27、30、9、2、35、7、26、32、2、23、0、13、1、6、31、3、28、4 5、18、12、2、9、14、17、21、19、22、15、20、24、11、10、16、25}。ここでは、n = 0、m = 35です。このシーケンスは_34
_を欠いており、2つの_2
_があります。
これは、O(m)時間であり、O(1)空間解である。
O(n)時間内およびO(1)空間内では、範囲外の値が容易に検出されるため、テストは範囲内に集中します。 (すべての値が有効な範囲_[n, n+m)
_)シーケンスにあることを意味します。それ以外の場合_{1, 34}
_は、反例です(Cバージョンの場合、 sizeof(int)== 4、数値の標準バイナリ表現)。
CとRuby version:_<<
_演算子の主な違いは、sizeof(int)が有限であるためCの値をローテーションしますが、Ruby数は結果に対応するために増加します。
Ruby:_1 << 100 # -> 1267650600228229401496703205376
_
C:_int n = 100; 1 << n // -> 16
_
Rubyの場合:_check_index ^= 1 << i;
_はcheck_index.setbit(i)
と同等です。同じ効果をC++で実装できます:vector<bool> v(m); v[i] = true;
_bool isperm_fredric(int m; int a[m], int m, int n)
{
/**
O(m) in time (single pass), O(1) in space,
no restriction on n,
?overflow?
a[] may be readonly
*/
int check_index = 0;
int check_value = 0;
int min = a[0];
for (int i = 0; i < m; ++i) {
check_index ^= 1 << i;
check_value ^= 1 << (a[i] - n); //
if (a[i] < min)
min = a[i];
}
check_index <<= min - n; // min and n may differ e.g.,
// {1, 1}: min=1, but n may be 0.
return check_index == check_value;
}
_
上記の関数の値は、次のコードに対してテストされました。
_bool *seen_isperm_trusted = NULL;
bool isperm_trusted(int m; int a[m], int m, int n)
{
/** O(m) in time, O(m) in space */
for (int i = 0; i < m; ++i) // could be memset(s_i_t, 0, m*sizeof(*s_i_t));
seen_isperm_trusted[i] = false;
for (int i = 0; i < m; ++i) {
if (a[i] < n or a[i] >= n + m)
return false; // out of range
if (seen_isperm_trusted[a[i]-n])
return false; // duplicates
else
seen_isperm_trusted[a[i]-n] = true;
}
return true; // a[] is a permutation of the range: [n, n+m)
}
_
入力配列は次のように生成されます:
_void backtrack(int m; int a[m], int m, int nitems)
{
/** generate all permutations with repetition for the range [0, m) */
if (nitems == m) {
(void)test_array(a, nitems, 0); // {0, 0}, {0, 1}, {1, 0}, {1, 1}
}
else for (int i = 0; i < m; ++i) {
a[nitems] = i;
backtrack(a, m, nitems + 1);
}
}
_
「nickf」からの回答は、配列がソートされていない場合は機能しませんvar_dump(testArray(array(5、3、1、2、4)、1、5)); // "duplicates"を与える!!!!
また、sum([n ... n + m-1])を計算するための式は正しくないように見えます...正しい式は(m(m + 1)/ 2-n(n-1)/ 2)です
したがって、1つのパスでm個の数値の積を計算でき、mも計算できます。そして、積modulo mかどうかを確認してください!パスの終わりにゼロです
何か足りないかもしれませんが、これが私の頭に浮かぶものです...
Pythonでのこのようなもの
my_list1 = [9,5,8,7,6]
my_list2 = [3,5,4,7]
デフ連続(my_list):
count = 0
prod = fact = 1
for num in my_list:
prod *= num
count +=1
fact *= count
if not prod % fact:
return 1
else:
return 0
連続印刷(my_list1)
連続印刷(my_list2)
HotPotato〜$ python m_consecutive.py
1
私は以下を提案します:
有限素数のセットP_1、P_2、...、P_Kを選択し、各P_iを法として入力シーケンス(最小値を差し引いたもの)の要素の出現を計算します。有効なシーケンスのパターンは既知です。
たとえば、17要素のシーケンスの場合、2を法として、[9 8]、3を法とする:[6 6 5]、5を法とする:[4 4 3 3 3]などのプロファイルが必要です。
複数のベースを使用してテストを組み合わせると、より正確な確率論的テストが得られます。エントリは整数サイズによって制限されているため、正確なテストを提供する有限の基底が存在します。これは、確率的な疑似素数性テストに似ています。
S_i is an int array of size P_i, initially filled with 0, i=1..K
M is the length of the input sequence
Mn = INT_MAX
Mx = INT_MIN
for x in the input sequence:
for i in 1..K: S_i[x % P_i]++ // count occurrences mod Pi
Mn = min(Mn,x) // update min
Mx = max(Mx,x) // and max
if Mx-Mn != M-1: return False // Check bounds
for i in 1..K:
// Check profile mod P_i
Q = M / P_i
R = M % P_i
Check S_i[(Mn+j) % P_i] is Q+1 for j=0..R-1 and Q for j=R..P_i-1
if this test fails, return False
return True
これを考えると-
サイズmのint配列を取るメソッドを記述します...
最大のintの値に等しいmの上限があると結論付けるのは公平だと思います(2 ^ 32が一般的です)。 つまり、mがintとして指定されていなくても、配列に重複を含めることができないという事実は、形成できる値の数を超えることはできないことを意味します32ビットは、mがintにも制限されることを意味します
そのような結論が許容できる場合は、(2 ^ 33 + 2)* 4バイト= 34,359,738,376バイト= 34.4GBの固定スペースを使用して、考えられるすべてのケースを処理することをお勧めします。 (入力配列とそのループに必要なスペースは数えません)。
もちろん、最適化のために、最初にmを考慮に入れ、必要な実際の量(2m + 2)* 4バイトのみを割り当てます。
これがO(1)スペース制約-に対して許容できる場合は、上記の問題の場合)-次に進みますアルゴリズムの提案... :)
仮定:mバイトの配列、正または負、4バイトが保持できる最大値。重複が処理されます。最初の値は、任意の有効な整数にすることができます。上記のようにmを制限します。
最初に、長さ2m-1のint配列を作成しますary、および3つのint変数を提供します:left、diff、およびright。 2m + 2になることに注意してください...
2番目、入力配列から最初の値を取得し、新しい配列のm-1の位置にコピーします。 3つの変数を初期化します。
Third、入力配列の残りの値をループし、反復ごとに以下を実行します。
これをコードに入れることにしましたが、うまくいきました。
C#を使用した実際のサンプルを次に示します。
public class Program
{
static bool puzzle(int[] inAry)
{
var m = inAry.Count();
var outAry = new int?[2 * m - 1];
int diff = 0;
int left = 0;
int right = 0;
outAry[m - 1] = inAry[0];
for (var i = 1; i < m; i += 1)
{
diff = inAry[i] - inAry[0];
if (diff > m - 1 + right || diff < 1 - m + left) return false;
if (outAry[m - 1 + diff] != null) return false;
outAry[m - 1 + diff] = inAry[i];
if (diff > left) left = diff;
if (diff < right) right = diff;
}
return true;
}
static void Main(string[] args)
{
var inAry = new int[3]{ 2, 3, 4 };
Console.WriteLine(puzzle(inAry));
inAry = new int[13] { -3, 5, -1, -2, 9, 8, 2, 3, 0, 6, 4, 7, 1 };
Console.WriteLine(puzzle(inAry));
inAry = new int[3] { 21, 31, 41 };
Console.WriteLine(puzzle(inAry));
Console.ReadLine();
}
}
グレッグ・ヒューギルの基数ソートの考え方が好きです。重複を見つけるには、この配列の値に対する制約を考慮して、O(N)時間でソートします。
インプレースO(1)スペースO(N)リストの元の順序を復元する時間の場合、その番号の実際のスワップ。フラグでマークするだけです:
//Java: assumes all numbers in arr > 1
boolean checkArrayConsecutiveRange(int[] arr) {
// find min/max
int min = arr[0]; int max = arr[0]
for (int i=1; i<arr.length; i++) {
min = (arr[i] < min ? arr[i] : min);
max = (arr[i] > max ? arr[i] : max);
}
if (max-min != arr.length) return false;
// flag and check
boolean ret = true;
for (int i=0; i<arr.length; i++) {
int targetI = Math.abs(arr[i])-min;
if (arr[targetI] < 0) {
ret = false;
break;
}
arr[targetI] = -arr[targetI];
}
for (int i=0; i<arr.length; i++) {
arr[i] = Math.abs(arr[i]);
}
return ret;
}
与えられた配列内にフラグを格納するのは一種の不正行為であり、並列化ではうまく機能しません。私はまだO(N)時間とO(log N)空間の配列に触れずにそれを行う方法を考えています。合計と最小値の合計をチェックしますsquares(arr [i]-arr.length/2.0)^ 2はうまくいくかもしれません。重複のない0 ... m配列について私たちが知っている特徴の1つは、均一に分散されていることです。これを確認するだけです。
証明できればいいのに。
階乗を含む上記の解決策は、階乗自体を格納するためにO(N)スペースを必要とします。N!> 2 ^ Nは、格納するためにNバイトを必要とします。
def test(a, n, m):
seen = [False] * m
for x in a:
if x < n or x >= n+m:
return False
if seen[x-n]:
return False
seen[x-n] = True
return False not in seen
print test([2, 3, 1], 1, 3)
print test([1, 3, 1], 1, 3)
print test([1, 2, 4], 1, 3)
これは、not in
に含まれる線形検索を考慮せず、最初の配列を1回だけ通過することに注意してください。 :)
python set
を使用することもできましたが、set
のパフォーマンス特性を考慮する必要がない単純なソリューションを選択しました。
更新:Smasheryは、「一定量のメモリ」を誤って解析したことを指摘しましたが、このソリューションは実際には問題を解決しません。
O(N)時間とO(1)重複を見つけるための余分なスペース:
public static boolean check_range(int arr[],int n,int m) {
for(int i=0;i<m;i++) {
arr[i] = arr[i] - n;
if(arr[i]>=m)
return(false);
}
System.out.println("In range");
int j=0;
while(j<m) {
System.out.println(j);
if(arr[j]<m) {
if(arr[arr[j]]<m) {
int t = arr[arr[j]];
arr[arr[j]] = arr[j] + m;
arr[j] = t;
if(j==arr[j]) {
arr[j] = arr[j] + m;
j++;
}
}
else return(false);
}
else j++;
}
説明:-
- Arr [i] = arr [i]-nによって範囲(0、m-1)に数値を移動します。範囲外の場合はfalseを返します。
- 各iについて、arr [arr [i]]が占有されていないかどうか、つまりmより小さい値であるかどうかを確認します
- もしそうならswap(arr [i]、arr [arr [i]])とarr [arr [i]] = arr [arr [i]] + m占有されていることを知らせる
- arr [j] = jの場合、単純にmを追加してjを増分します
- arr [arr [j]]> = mの場合、占有されているため、現在の値が重複しているため、falseを返します。
- arr [j]> = mの場合はスキップ
私の現在のベストオプション
def uniqueSet( array )
check_index = 0;
check_value = 0;
min = array[0];
array.each_with_index{ |value,index|
check_index = check_index ^ ( 1 << index );
check_value = check_value ^ ( 1 << value );
min = value if value < min
}
check_index = check_index << min;
return check_index == check_value;
end
O(n) and Space O(1)
私はそれを失敗させる可能性のあるブルートフォースの組み合わせのスクリプトを書きましたが、何も見つかりませんでした。この関数に反する配列がある場合は、それを伝えてください。 :)
@ J.F。セバスチャン
これは真のハッシュアルゴリズムではありません。技術的には、その「見られた」値の非常に効率的なパックされたブール配列。
ci = 0, cv = 0
[5,4,3]{
i = 0
v = 5
1 << 0 == 000001
1 << 5 == 100000
0 ^ 000001 = 000001
0 ^ 100000 = 100000
i = 1
v = 4
1 << 1 == 000010
1 << 4 == 010000
000001 ^ 000010 = 000011
100000 ^ 010000 = 110000
i = 2
v = 3
1 << 2 == 000100
1 << 3 == 001000
000011 ^ 000100 = 000111
110000 ^ 001000 = 111000
}
min = 3
000111 << 3 == 111000
111000 === 111000
これのポイントは、ほとんどの場合、問題のほとんどのケースを「偽造」するために、重複を使用してそうすることです。このシステムでは、XORは、同じ値を2回使用したことでペナルティを課し、代わりに0回実行したと想定します。
もちろん、ここでの警告:
$x
の( 1 << $x > 0 )
の最大値によって制限されます最終的な効果は、基盤となるシステムが以下の機能をどのように実装するかに依存します。
編集注意:上記のステートメントは紛らわしいようです。 「整数」が無限精度のレジスタであり、O(1)時間で^ bを実行できる完全なマシンであると仮定します。
しかし、これらの仮定に失敗すると、単純な数学のアルゴリズムの複雑さを問い始める必要があります。