web-dev-qa-db-ja.com

配列にn ... n + mが含まれているかどうかを判断するアルゴリズム?

私はこの質問を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)スペース?

45
Kyle Cronin

a[i] % a.length の代わりに a[i]問題を減らして、数値を持っているかどうかを判断する必要があります0a.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を入れ替えようとする試みがあるとすぐにわかります。

5
Stephen Denne

他のソリューションがすべての値の合計を使用するのはなぜですか? O(n)アイテムを1つの数値に加算すると、技術的にはO(1)スペース以上を使用しているので、これは危険だと思います。

より簡単な方法:

ステップ1、重複がないかどうかを確認します。 O(1) spaceでこれが可能かどうかはわかりません。とにかく、重複がある場合はfalseを返します。

ステップ2、リストを反復処理し、lowestおよびhighest項目を追跡します。

ステップ3、(最高-最低)はmに等しいですか?その場合は、trueを返します。

2
andy

ワンパスアルゴリズムでは、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.

2
Dave
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;
}
1
yvette

CでのHazzenのアルゴリズム実装

#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
ignoramous

しばらく前に、電話会社で働いていた誰かから非常に賢い並べ替えアルゴリズムについて聞きました。膨大な数の電話番号を分類する必要がありました。一連のさまざまな並べ替え戦略を実行した後、最終的に非常にエレガントなソリューションを見つけました。ビット配列を作成し、ビット配列へのオフセットを電話番号として処理しただけです。次に、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

そうすれば、すぐに失敗します。

1
Kevin Day

私が間違っている場合は投票してください。重複があるかどうか、または分散を使用していないかどうかは判断できます。事前に平均を知っているため(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を返します。

1
user17182

あなたが配列の長さだけを知っていて、配列を変更することが許可されていると仮定すると、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;
    }
}
1
caskey

ここにO(n)の実用的な解決策があります

これは、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
1
nickf

XORアルゴリズム の反例。

(コメントとして投稿することはできません)

@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);
}
0
jfs

したがって、入力配列の変更を必要とせず、一定のスペースをとるO(n ^ 2)を取るアルゴリズムがあります。

まず、nmを知っていると仮定します。これは線形操作であるため、複雑さが増すことはありません。次に、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;

追加情報を保存することで、これを最適化できます。

  1. 各要素からのサイクルの長さの合計を記録します。サイクルがその要素の前の要素を訪問しない限り、それをsum_of_stepsと呼びます。
  2. すべての要素について、ステップm-sum_of_stepsノードのみがアウトになります。開始要素に戻らず、開始要素の前の要素にアクセスしない場合、重複する要素を含むループが見つかり、falseを返す可能性があります。

これはまだO(n ^ 2)です。 {1, 2, 3, 0, 5, 6, 7, 4}ですが、少し高速です。

0
MSN

note:このコメントは質問の元のテキストに基づいています(それ以降修正されています)

上記のように正確に質問が出され(そして単なるタイプミスではない)、サイズnの配列の場合、関数は(True/False )配列が1 ... n + 1の数字で構成されている場合、

...その場合、すべての数値1 ... n + 1を持つ配列はnではなくサイズn + 1になるため、答えは常にfalseになります。したがって、質問はO(1)で回答できます。 :)

0
CaptSolo

連続する配列[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
0
xtofl

配列にはN個の数値が含まれており、2つの数値の合計が特定の数値Kになるかどうかを判断する必要があります。たとえば、入力が8,4、1,6で、Kが10の場合、答えはイエスです(4および6 )。数値は2回使用できます。以下をせよ。 a。この問題を解決するには、O(N2)アルゴリズムを指定します。b。この問題を解決するには、O(N log N)アルゴリズムを指定します。(ヒント:最初にアイテムを並べ替えます。実行後、線形時間で問題を解決できます。)c。両方のソリューションをコーディングし、アルゴリズムの実行時間を比較します。

0
khaled

他のソリューションがすべての値の合計を使用するのはなぜですか? O(n)アイテムを1つの数値に加算すると、技術的にはO(1)スペース以上を使用しているので、これは危険だと思います。

O(1)は、nの数によって変化しない定数空間を示します。定数であれば1変数でも2変数でもかまいません。なぜO(1) space)を超えていると言うのですか?一時変数に累積してn個の数値の合計を計算する場合、とにかく1つの変数を使用することになります。

システムではまだコメントを書き込むことができないため、回答にコメントする。

更新(コメントへの返信):この回答では、O(1) space "space"または "time"が省略されている場合は常にspaceを意味します。引用されたテキストは、以前の回答の一部です。これは返信です。

0
CaptSolo

数値の合計を知りたい場合[n ... n + m - 1]この方程式を使用してください。

var sum = m * (m + 2 * n - 1) / 2;

これは、nが10進数であっても、正または負の任意の数値で機能します。

0
nickf

b3の疑似コードのCバージョン

(疑似コードの誤解を避けるため)

カウンターの例:{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));
}
0
jfs

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)
0
jfs

ciphwnはそれを正しく持っています。それはすべて統計に関係しています。質問されるのは、統計的に言えば、数列が離散的な均一分布を形成するかどうかです。離散均一分布は、可能な値の有限セットのすべての値が等しく可能性がある場所です。幸い、離散セットが均一かどうかを判断するためのいくつかの有用な式があります。まず、セットの平均を決定するには、(a..b)は(a + b)/ 2で、分散は(n.n-1)/ 12です。次に、特定のセットの分散を決定します。

variance = sum [i=1..n] (f(i)-mean).(f(i)-mean)/n

そして、予想される分散と比較します。これには、平均を決定するためとデータの分散を計算するための2回のデータパスが必要です。

参照:

0
Skizz

おっとっと!私は重複した質問に巻き込まれ、すでに同じ解決策をここで見ませんでした。そして、やっとオリジナルなことができたと思いました!これは私が少し喜んだときの歴史的アーカイブです:


ええと、このアルゴリズムがすべての条件を満たすかどうかは確実ではありません。実際、私が試したいくつかのテストケースを超えて機能することを検証していません。私のアルゴリズムに問題があるとしても、うまくいけば私のアプローチがいくつかの解決策を引き起こすでしょう。

このアルゴリズムは、私の知る限り、一定のメモリで動作し、アレイを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;

}
0
erisco

Kent Fredric's Ruby solution のCバージョン

(テストを容易にするため)

反例(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);
    }
}
_
0
jfs

「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)です

0
lalitm

M連番の商品がmで割り切れます! [m階乗]


したがって、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

0
Sagar Mehta

私は以下を提案します:

有限素数のセット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
0
Eric Bainville

これを考えると-

サイズ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変数を提供します:leftdiff、およびright。 2m + 2になることに注意してください...

2番目、入力配列から最初の値を取得し、新しい配列のm-1の位置にコピーします。 3つの変数を初期化します。

  • セットary [m-1]-nthVal // n = 0
  • セット = 差分 = =

Third、入力配列の残りの値をループし、反復ごとに以下を実行します。

  • セットdiff = nthVal-ary [m-1]
  • if(diff> m-1 + right || diff <1-m + left)return false// 立入禁止で
  • if(ary [m-1 + diff]!= null)return false //複製
  • セットary [m-1 + diff] = nthVal
  • if(diff> leftleft = diff //左境界をさらに右に制約します
  • if(diff <rightright = diff //右境界をさらに左に制約します

これをコードに入れることにしましたが、うまくいきました。

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();
    }

}
0
hurst

グレッグ・ヒューギルの基数ソートの考え方が好きです。重複を見つけるには、この配列の値に対する制約を考慮して、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バイトを必要とします。

0
GenericKen
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は、「一定量のメモリ」を誤って解析したことを指摘しましたが、このソリューションは実際には問題を解決しません。

0
Greg Hewgill

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++;

        }

説明:-

  1. Arr [i] = arr [i]-nによって範囲(0、m-1)に数値を移動します。範囲外の場合はfalseを返します。
  2. 各iについて、arr [arr [i]]が占有されていないかどうか、つまりmより小さい値であるかどうかを確認します
  3. もしそうならswap(arr [i]、arr [arr [i]])とarr [arr [i]] = arr [arr [i]] + m占有されていることを知らせる
  4. arr [j] = jの場合、単純にmを追加してjを増分します
  5. arr [arr [j]]> = mの場合、占有されているため、現在の値が重複しているため、falseを返します。
  6. arr [j]> = mの場合はスキップ
0
Vikram Bhat

私の現在のベストオプション

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回実行したと想定します。

もちろん、ここでの警告:

  1. 入力配列の長さと配列の最大値の両方が、$x( 1 << $x > 0 )の最大値によって制限されます
  2. 最終的な効果は、基盤となるシステムが以下の機能をどのように実装するかに依存します。

    1. 1ビットn桁右にシフトします。
    2. xor 2レジスタ。 (「レジスタ」は、実装に応じて、いくつかのレジスタにまたがることがあります)

    編集注意:上記のステートメントは紛らわしいようです。 「整数」が無限精度のレジスタであり、O(1)時間で^ bを実行できる完全なマシンであると仮定します。

しかし、これらの仮定に失敗すると、単純な数学のアルゴリズムの複雑さを問い始める必要があります。

  • 1 == 1はどのくらい複雑ですか?確かにそれはO(1)毎回正しいはずです。
  • 2 ^ 32 == 2 ^ 32についてはどうでしょう。
  • O(1)? 2 ^ 33 == 2 ^ 33?ここで、レジスターのサイズとその基礎となる実装についての質問があります。
  • 幸い、XORと==は並行して実行できるため、無限の精度を想定し、無限の精度に対処するように設計されたマシンが想定されている場合、XOR =と==の値に関係なく一定の時間がかかります(幅が無限であるため、0のパディングは無限になります。明らかに、これは存在しません。ただし、000000から000100に変更しても、メモリ使用量は増えません。
  • しかし、一部のマシンでは、(1 << 32)<< 1 willはより多くのメモリを消費しますが、その量は不確かです。
0
Kent Fredric