昨日、私は清潔な洗濯物と靴下をペアにしていて、私がしていた方法を考え出しましたそれは非常に効率的ではありません。私は素朴な検索をしていました - 1つの靴下を選び、その対を見つけるために山を「繰り返し」ました。これにはn/2 * n/4 = nの繰り返しが必要です2平均して/ 8靴下。
私はコンピューター科学者として自分ができることを考えていましたか?ソート(サイズ/色/ ...による)はもちろんO(NlogN)解決法を達成するために頭に浮かんだ。
私は自分の靴下を複製することができないので、ハッシュまたは他のインプレース解決策は選択肢ではありません(できればいいかもしれませんが)。
だから、質問は基本的にです:
2n
要素を含むn
組の靴下がある場合(各靴下がちょうど1つの一致する組を持っていると仮定します)、それらを対数的余剰スペースまで効率的に組み合わせるための最良の方法は何ですか? (必要ならその量の情報を覚えていると思います。)
私は以下の側面に対処する答えをいただければ幸いです。
並べ替えソリューションが提案されていますが、並べ替えは少し多すぎます:順序は必要ありません。 等式グループが必要です。
hashing で十分(かつ高速)です。
この種の再帰的なハッシュ分割は、 SQL Server によって実際に行われています。巨大なデータセットに対してハッシュ結合またはハッシュ集計が必要な場合です。ビルド入力ストリームを独立した多くのパーティションに分配します。このスキームは、任意の量のデータと複数のCPUに直線的にスケーリングします。
十分なバケットを提供する分散キー(ハッシュキー)を見つけることができれば、再帰的なパーティション分割は必要ありません。残念ながら、私は靴下にそのような特性があるとは思わない。
各靴下に「PairID」という整数がある場合、PairID % 10
(最後の数字)に従って10個のバケットに簡単に配布できます。
私が考えることができる最高の現実世界のパーティション分割は、長方形の山を作成することです。一方の次元は色で、もう一方はパターンです。なぜ長方形なのか?パイルへのO(1)ランダムアクセスが必要だからです。 (3D cuboid も機能しますが、あまり実用的ではありません。)
更新:
parallelism についてはどうですか?複数の人間が靴下をより速く合わせることができますか?
element distinctness problem はどうですか?記事が述べているように、要素の明確さの問題はO(N)
で解決できます。これは靴下の問題についても同じです(また、O(N)
、1つの配布ステップのみが必要な場合(人間が計算が苦手なので複数のステップを提案しました。md5(color, length, pattern, ...)
で配布する場合は1つのステップで十分です。すなわち、完全なハッシュすべての属性の))。
明らかに、O(N)
より速く進むことはできないので、最適な下限に到達しました。
出力はまったく同じではありませんが(ある場合はブール値、別の場合は靴下のペア)、漸近的な複雑さは同じです。
人間の脳のアーキテクチャは現代のCPUとはまったく異なるので、この質問は実用的な意味を持ちません。
「一致するペアを見つける」ことは、大きすぎない集合に対して1つの操作になることができるという事実を使用して、人間はCPUアルゴリズムに勝つことができます。
私のアルゴリズム:
spread_all_socks_on_flat_surface();
while (socks_left_on_a_surface()) {
// Thanks to human visual SIMD, this is one, quick operation.
pair = notice_any_matching_pair();
remove_socks_pair_from_surface(pair);
}
少なくともこれが私が実生活で使用しているものであり、私はそれが非常に効率的であると思います。欠点は、平らな面が必要なことですが、通常は豊富です。
ケース1 :すべての靴下は同一です(これは私が実際の生活の中でところで行っていることです)。
2つ選んでペアにします。一定の時間.
ケース2 :定数の組み合わせ(所有権、色、サイズ、テクスチャなど)があります。
基数ソート を使用します。比較は必要ないので、これは線形時間にすぎません。
ケース3 :組み合わせの数が事前にわからない(一般的な場合)。
2つの靴下がペアになっているかどうかを確認するために比較をする必要があります。 O(n log n)
比較ベースのソートアルゴリズムの1つを選んでください。
しかし、実際の靴下の数が比較的少ない(一定)場合、これらの理論的に最適なアルゴリズムはうまく機能しません。理論的には2次時間が必要な順次検索よりもさらに時間がかかる場合があります。
非アルゴリズム的な回答ですが、実行すると「効率的」になります。
ステップ1)既存の靴下をすべて捨てる
ステップ2) Walmart に行き、10 - nの白のパケットとmの黒のパケットでそれらを購入します。日常生活の中で他の色は必要ありません。
それでも時々、私はこれをやり直さなければなりません(なくなった靴下、破損した靴下など)、そして私は完璧な良い靴下をあまりにも頻繁に捨てるのが嫌いです(そして私は彼らが同じ靴下の参照を売り続けたことを望みます!別のアプローチ.
アルゴリズムの答え:
あなたがしているように、あなたがしているように、あなたがしているように、靴下の2番目のスタックのために1つの靴下だけを描くならあなたのオッズはかなり低いです。
なぜ5人?通常、人間は作業メモリ内の5つから7つの異なる要素を覚えています - 人間の RPN スタックと少し似ています - 5が安全なデフォルトです。
2n-5のスタックから1つ選ぶ。
描いた5つの中でマッチ(視覚的なパターンマッチング - 人間は小さいスタックでそれを得意とする)を探し、見つからなければそれをあなたの5つに加える。
ランダムに靴下をスタックから選び、あなたの5 + 1靴下と比較して試合をしましょう。スタックが大きくなるにつれて、パフォーマンスは低下しますが、確率は上がります。はるかに高速。
あなたがマッチの50%のオッズのためにあなたが引かなければならないサンプルの数を計算するために公式を書き留めてください。 IIRCそれは超幾何法則です。
私は毎朝それをして、めったに3つ以上の引き分けを必要としません - しかし、私はn
のような白い靴下のm
のようなペア(およそ10の、失ったものを与えるか取る)を持っています。今、あなたは私の株の大きさを見積もることができます:-)
_ btw _ 、ペアを必要とするたびにすべての靴下を並べ替えるためのトランザクションコストの合計が、一度行って靴下を束縛するよりもはるかに少ないことがわかりました。靴下を縛る必要がないのでジャストインタイムの方が効果的です。また、限界リターンが減少することもあります(つまり、洗濯物のどこかに必要な2つまたは3つの靴下を探し続けます)。あなたの靴下のマッチングを完了するために、あなたはそれについて時間を失います)。
私がすることは私が最初の靴下を拾い上げてそれを下に置くことです(例えば、洗濯ボウルの端に)。それから私は別の靴下を拾い上げて、それが最初の靴下と同じかどうか確認します。もしそうなら、私はそれらを両方削除します。そうでない場合は、最初の靴下の横に置きます。それから私は3番目の靴下を拾い上げて、(彼らがまだそこにいるならば)最初の2つとそれを比較します。等。
この方法は、靴下を「取り除く」ことが選択肢であると仮定すると、かなり簡単に配列に実装することができます。 実際には、靴下を「削除」する必要すらありません。靴下を並べ替える必要がない場合(下記を参照)、それらを移動して、すべての靴下が配列にペアで配置された配列になるだけです。
靴下の唯一の操作は同等かどうかを比較することであると仮定すると、このアルゴリズムは基本的にはまだnです。2 アルゴリズム、私は平均的なケースについては知らないが(それを計算することを学んだことはない)。
並べ替えは、もちろん効率を向上させます。特に、実際の生活では、他の2つの靴下の間に靴下を簡単に「挿入」することができます。計算でも同じことがツリーでも実現できますが、それは余分なスペースです。そしてもちろん、私たちはNlogNに戻ってきました(あるいは、ソート基準によって同じであるが同じペアからではないいくつかの靴下があるならば、もう少しです)。
それ以外には、私は何も考えることができませんが、この方法は実生活ではかなり効率的です。 :)
これは間違った質問をしています。尋ねるべき正しい質問は、なぜ私は靴下を分類するのに時間を費やすのですか?あなたが選択したX通貨単位のためのあなたの自由時間を評価するとき、それは年間ベースでいくらかかりますか?
そして多くの場合、これは単に any 空き時間ではなく、 morning 空き時間です。これは、ベッドで過ごすか、コーヒーを飲みながら、あるいは少し早く出発して渋滞に巻き込まれていない。
一歩後退して、問題を回避する方法を考えるのは良いことです。
そして方法があります!
あなたが好きな靴下を見つけましょう。さまざまな照明条件での色、全体的な品質と耐久性、さまざまな気候条件での快適さ、臭気の吸収など、関連するすべての機能を考慮に入れます。また重要なのは、それらは貯蔵中に弾力性を失うべきではないので、天然の布地は良く、そしてそれらはプラスチック包装で利用可能であるべきである。
左右の足の靴下の間に違いがない場合はそれが良いですが、それは重要ではありません。靴下が左右対称である場合、ペアを見つけることはO(1)操作であり、靴下をソートすることは近似的なO(M)操作です。靴下が散らばっているあなたの家、理想的にはいくつかの小さな定数。
左右の靴下が異なるファンシーペアを選択した場合、左右の足のバケツにフルバケットソートをするとO(N + M)がかかります。ここで、Nは靴下の数、Mは上記と同じです。誰かが最初のペアを見つけるための平均反復の公式を与えることができますが、ブラインドサーチでペアを見つけるための最悪のケースはN/2 + 1であり、これは合理的なNに対して天文学的にありそうもないケースになります。 Mk1 Eyeball でソートされていない靴下の山をスキャンするときの認識アルゴリズムとヒューリスティック。
したがって、O(1)靴下ペアリング効率を達成するためのアルゴリズム(対称靴下を仮定)は、次のとおりです。
あなたはあなたがあなたの人生の残りのために、あるいはおそらくあなたが引退して暖かい気候に再び動く必要がなくなるまでに何組の靴下を必要とするかを見積もる必要があります。あなたが若いなら、私たち全員が私たちの家に靴下仕分けロボットを持つまでにかかる時間を見積もることもできます。そして問題全体は無関係になります。
選択した靴下をまとめて注文する方法とその費用を確認し、それらを配送するかどうかを確認する必要があります。
靴下を注文する!
あなたの古い靴下を取り除きます。
もう一つのステップ3は、何年にもわたって同じ組のたぶん安い靴下を何組か購入するコストを比較して、靴下を分類するコストを追加することを含みます。また、倉庫内の靴下は株価インフレ率で価値が増加します。これは、多くの投資で得られるものを上回るものです。それからまた貯蔵費があるが、ソックスはクローゼットの上の棚に実際に多くのスペースを取らない。
問題が解決しました。だから、新しい靴下を手に入れ、古い靴下を捨てたり寄付したりして、毎日あなたの人生のためにお金と時間を節約していることを知ってから幸せに暮らしてください。
各靴下に触れる必要があるため、理論上の制限はO(n)です(既に何らかのペアになっている場合を除く)。
基数ソート でO(n)を実現できます。バケットの属性をいくつか選ぶだけでいいのです。
限られた数の属性を選ぶことができても、各ペアを一意に識別できる十分な属性がある場合は、O(k * n)で行う必要があります。kが次のように考えられる場合、O(n)です。限られています。
実用的な解決策として:
あなたが8色と平均分布で1000の靴下があるならば、あなたはc * n時間で各125の靴下の4つの山を作ることができます。 5ソックスのしきい値を使用すると、6回の実行ですべての山を並べ替えることができます。 (右の山に靴下を投げるために2秒を数えると、4時間以内に少し時間がかかります。)
あなたがたった60の靴下、3色と2種類の靴下(あなたの/あなたの妻のもの)を持っているなら、あなたは1回の実行で10個の靴下のすべての山をソートすることができます(やはりthreshold = 5)。 (2秒カウントすると2分かかります)。
最初のバケツソートはあなたのプロセスをスピードアップするでしょう、なぜならあなたがc*n*log(k)
仕事をする必要があるよりあなたのn個の靴下をc*n
時間でk個のバケットに分割するからです。 (しきい値を考慮に入れていません)。ですから、結局のところ、あなたはn*c*(1 + log(k))
の作業をすることになります。ここでcはパイルに靴下を投げる時です。
このアプローチはおおよそc*x*n + O(1)
であればどのlog(k) < x - 1
メソッドに比べても有利です。
コンピュータサイエンスではこれが役に立つことがあります:私たちはn もの の集まり、それらの順序(長さ)そして同値関係(追加情報、例えば靴下の色)を持っています。同値関係により、元のコレクションのパーティションを作成することができます。すべての同値クラスでは、順序は依然として維持されます。 thing の等価クラスへのマッピングはO(1)で行うことができるので、各項目をクラスに割り当てるにはO(n)のみが必要です。これで、追加情報を使用して、あらゆるクラスをソートするためにあらゆる方法で進むことができます。利点は、データセットがすでにかなり小さいことです。
複数の同値関係がある場合は、テクスチャ上のすべてのパイルパーティション内ではなく、長さ順でソートするよりも、カラーパイルを作成するという方法で、メソッドをネストすることもできます。サイズがほぼ等しい2つ以上の要素を持つパーティションを作成する等価関係は、ソートよりも速度が向上します(ただし、ソックをその山に直接割り当てることができれば)、ソートは小さなデータセットで非常に速く行われます。
この質問は実際には非常に哲学的です。本質的には、問題を解決する人々の力(脳の「ウェットウェア」)がアルゴリズムによって達成できるものと同等であるかどうかです。
靴下ソートの明らかなアルゴリズムは次のとおりです。
Let N be the set of socks that are still unpaired, initially empty
for each sock s taken from the dryer
if s matches a sock t in N
remove t from N, bundle s and t together, and throw them in the basket
else
add s to N
今、この問題のコンピューターサイエンスは、すべての手順についてです
人間はさまざまな戦略を使用してこれらを実現します。 ヒューマンメモリis連想 、保存された値の特徴セットが対応する値自体とペアになっているハッシュテーブルのようなもの。たとえば、「赤い車」の概念は、人が思い出すことができるすべての赤い車にマッピングされます。完璧な記憶力を持つ人は、完璧なマッピングを持っています。ほとんどの人はこの点で不完全です(そして他のほとんどの人)。連想マップの容量は限られています。マッピングはbleepさまざまな状況(1つのビールが多すぎる)で存在しなくなり、エラーで記録される場合があります(「彼女の名前はNettieではなくBettyでしたが」 )、または、真実が変わったことを観察しても上書きされません(「パパの車」は、赤いカマロと交換したことを実際に知っていたときに「オレンジ色の火の鳥」を呼び起こします)。
靴下の場合、完全なリコールとは、靴下を見るs
が常にその兄弟t
のメモリを生成し、t
を見つけるのに十分な情報(アイロン台上)を含むことを意味します一定の時間で。写真の記憶を持っている人は、確実に一定の時間で1と2の両方を達成します。
完璧な記憶力を持たない人は、追跡する能力内の機能に基づいて、サイズ(パパ、ママ、赤ちゃん)、色(緑がかった、赤みがかったなど)、パターン(アーガイル、プレーンなど)に基づいて、いくつかの常識等価クラスを使用する可能性があります、スタイル(フット、膝丈など)。そのため、アイロン台はカテゴリのセクションに分割されます。これにより、通常、メモリによってカテゴリを一定時間で見つけることができますが、カテゴリ「バケット」を介した線形検索が必要です。
記憶や想像力がまったくない人(申し訳ありません)は、靴下を1つの山に留めて、山全体を直線的に検索します。
きちんとしたフリークは、誰かが示唆したように、ペアに数値ラベルを使用する場合があります。これにより、完全な順序付けへの扉が開かれます。これにより、人間は、CPUを使用した場合とまったく同じアルゴリズム(バイナリ検索、ツリー、ハッシュなど)を使用できます。
したがって、「最良の」アルゴリズムは、それを実行しているウェットウェア/ハードウェア/ソフトウェアの品質と、ペアに完全な順序を課すことによって「チート」する意思に依存します。確かに「ベスト」メタ-アルゴリズムは、世界最高の靴下選別機を雇うことです。巨大なセットNを入手してすぐに保存できる人または機械一定時間のルックアップ、挿入、削除を行う1-1連想メモリのsock属性セット。このような人と機械の両方を調達できます。持っている場合は、N個のペアに対してO(N)時間ですべての靴下をペアリングできます。これは最適です。合計注文タグを使用すると、標準のハッシュを使用して、人間またはハードウェアのコンピューターで同じ結果を得ることができます。
あなたは間違った問題を解決しようとしています。
解決策1: あなたがあなたの洗濯かごに汚れた靴下を入れるたびに、小さな結び目にそれらを結びます。そうすれば、洗濯後に分別をする必要がなくなります。 Mongoデータベースにインデックスを登録するのと同じように考えてください。将来的にCPUを節約するために少し作業を進めます。
解決策2: 冬なら、靴下を履く必要はありません。私たちはプログラマーです。うまくいく限り、だれも知る必要はありません。
解決策3: /仕事を広げる。 UIをブロックせずに、このような複雑なCPUプロセスを非同期的に実行する必要があります。その山の靴下を持っていって、バッグに詰めます。あなたがそれを必要とするときだけペアを探す。そうすれば、作業量はそれほど目立たなくなります。
お役に立てれば!
コスト:ムービングソックス - >高い、ラインでソックスを探す/探す - >小さい
私たちがやりたいことは、移動数を減らし、検索数で補うことです。また、ホモサピエンスのマルチスレッド環境を利用して、ディシジョンキャッシュにもっと多くのものを保持することもできます。
X =あなたのもの、Y =あなたの配偶者
すべての靴下の山Aから:
2つの靴下を選び、対応するX靴下をX線に配置し、Y靴下を次の使用可能な位置に配置します。
Aが空になるまで行います。
各行XとY
最初の靴下を一列に並べて、対応する靴下が見つかるまでその線に沿って検索します。
靴下の対応する完成ラインに入れてください。
キャッシングメモリが十分に大きいので、どちらかの靴下が現在観察している線上の靴下と一致するかどうかをすばやく確認できます。もしあなたが3本の腕を持つことが十分に幸運であるならば、あなたはたぶん主題の記憶が十分に大きいとすれば3つの靴下を同時に解析することができます。
XとYの両方が空になるまで行います。
完了
ただし、これは選択のソートと同じくらい複雑であるため、I/O(動く靴下)と検索(靴下を探すためのライン)の速度のためにかかる時間ははるかに少なくなります。
これが、比較ベースモデルのオメガ(n log n)下限です。 (唯一の有効な操作は2つの靴下を比較することです。)
あなたの2n靴下がこのように配置されていることを知っているとします。
p1 p2 p3 ... pn pf(1) pf(2) ... pf(n)
ここで、fは集合{1,2、...、n}の未知の順列です。これを知っていても問題はさらに困難にはなりません。 nがあります!可能な出力(前半と後半の一致)、つまりlog(n!)= Omega(n log n)の比較が必要です。これはソートによって得られます。
要素の識別性の問題への接続に関心があるので、出力がバイナリのyes/noなので、要素の識別性の限界Omega(n log n)を証明するのは困難です。ここで、出力は一致している必要があり、可能な出力の数は適切な範囲を取得するのに十分です。しかし、要素の区別に関連した変形があります。あなたが2nの靴下を与えられて、それらがユニークに対になることができるかどうか疑問に思うと仮定してください。次のように送信すると、EDから削減を得ることができます1、2、...、n)から(a)1、1、2、2、...、n、n) (括弧付きでは、EDの硬さの証明は非常に興味深い、 トポロジーを介して 。)
オメガがあるべきだと思う(n2等価テストのみを許可している場合は、元の問題に限定されます。私の直感は次のとおりです。テストの後にEdgeを追加するグラフについて考えてみます。グラフが密集していない場合、出力は一意に決定されないと主張します。
p靴下のペア(n = 2p個々の靴下)の場合、これは実際に行う方法です。
このスキームの最悪のシナリオは、靴下のすべてのペアが十分に異なるため、完全に一致する必要があり、最初に選択するn/2靴下はすべて異なるということです。これはあなたのO(n2)シナリオ、それは極端にありそうもない。靴下のユニークなタイプの数tがペアの数p = n/2より少なく、各タイプの靴下が十分に似ている場合(通常、関連する用語)そのタイプの靴下は他の靴下とペアにすることができ、上記で推測したように、あなたが比較しなければならない靴下の最大数はtです。 pull willペアになっていない靴下のいずれかと一致します。このシナリオは、平均的な靴下引き出しでは最悪の場合よりもはるかに可能性が高く、最悪の場合の複雑さをO(n * t)に減らします。通常はt << n。
実社会のアプローチ:
できるだけ速やかに、未選別の杭から1つずつ靴下を取り除き、あなたの前の杭に置きます。パイルは、すべての靴下が同じ方向を向くように、ややスペース効率をよくして配置する必要があります。杭の数はあなたが簡単に到達できる距離によって制限されます。靴下を置く杭の選択は、可能な限り迅速に、靴下を明らかに靴下のような杭の上に置くことによって行うべきです。時折のタイプI(それが属していない杭に靴下を置く)またはタイプII(靴下のような既存の杭があるときに自分の杭に靴下を置く)のエラーは許容できます - 最も重要な考慮は_です速度。すべての靴下が山になったら、すばやく複数靴下の山を通り抜けてペアを作り、それらを取り除きます(これらは引き出しに向かっています)。杭に一致しない靴下がある場合は、できるだけ速く(できるだけ速く拘束内で)杭を重ねてください。すべてのマルチソックパイルが処理されたら、タイプIIエラーのためにペアにされなかった残りのペアソックスをマッチさせます。 Whoosh、これでおしまいです - そして、私は靴下をたくさん持っていて、大部分が汚れるまでそれらを洗わないでください。もう1つの実用上の注意:私は1対の靴下のうちの1つの上を反対側にひっくり返して、それらの弾力性を利用して、引き出しに運ばれている間と引き出しの中で一緒にいます。
あなたの質問から、それはあなたが洗濯物に関する多くの実際の経験を持っていないことは明らかです:)。少数のペアリング不可能な靴下でうまく機能するアルゴリズムが必要です。
今までの答えは私たちの人間のパターン認識能力をうまく利用していません。セットのゲームはこれをうまくやる方法の手がかりを提供します:あなたが両方ともよくそれらをよく認識して、あなたの手で彼らに簡単に手が届くことができるように二次元空間にすべての靴下を置いてください。これはあなたを約120×80 cmほどの面積に制限します。そこからあなたが認識したペアを選択して削除します。空きスペースに余分な靴下を入れて繰り返します。あなたが簡単に認識できる靴下を持っている人のために洗うなら(小さな子供たちが頭に浮かぶ)、あなたは最初にそれらの靴下を選ぶことによって基数ソートをすることができます。このアルゴリズムは、シングルソックスの数が少ない場合にのみ有効です。
最初の靴下を手に取り、テーブルの上に置きます。今すぐ別の靴下を選ぶ。最初に選んだものと一致する場合は、最初のものの上に置きます。そうでない場合は、最初のテーブルから少し離してテーブルの上に置きます。 3番目の靴下を選びます。前の2つのいずれかと一致する場合は、それらの上に配置するか、または3つ目から少し離れたところに配置します。あなたがすべての靴下を拾うまで繰り返します。
ペアリングがチューリングによるものでもランダムアクセスマシンによるものでもなく、ペアリングがパイルからペアリングすることがいかに効率的であるかを言うために、我々は最初にマシンを定義しなければなりません。アルゴリズム解析.
機械は人間と呼ばれる現実世界の要素の抽象化です。それは一組の目を通して環境から読むことができます。そして私達の機械モデルは2本の腕を使用して環境を操作することができます。論理演算と算術演算は、私たちの頭脳を使って計算されます(うまくいけば;-))。
これらの機器を使って実行できるアトミック操作の本質的な実行時間も考慮する必要があります。物理的な制約のために、腕または眼によって行われる操作は、一定ではない時間複雑性を有する。これは、無限大の靴下を腕で動かすことも、無限大の靴下の上の一番上の靴下を見ることもできないためです。
しかしながら、力学的物理学は私達にいくつかの良い点をも与えます。私たちはせいぜい1つの靴下を腕で動かすことに限定されていません。私達はそれらの全体のカップルを一度に動かすことができます。
したがって、前の分析に応じて、以下の操作を降順で使用する必要があります。
人々は靴下の量が非常に限られているという事実を利用することもできます。だから環境の変更は山の中のすべての靴下を巻き込むことができます。
だからここに私の提案です:
靴下を床の上に広げると靴下によっては他の靴下が隠れる可能性があるため、操作4が必要です。これがアルゴリズムの分析です。
アルゴリズムは高い確率で終了します。これは、ステップ番号2で靴下のペアを見つけることができないという事実によるものです。
n
組の靴下のペアリングに関する次の実行時分析では、2n
靴下の少なくとも半分がステップ1の後に隠されていないと仮定します。そのため、平均的なケースではn/2
組を見つけることができます。これは、ステップ4のループがO(log n)
回実行されることを意味します。ステップ2はO(n^2)
回実行されます。だから我々は結論を下すことができます:
O(ln n + n)
環境修正が含まれます(ステップ1 O(ln n)
と床から靴下の各ペアを選ぶ)O(n^2)
環境読み取りを含みます。O(n^2)
論理演算と算術演算を含みます。そのため、実行時の複雑さはO(r*n^2 + w*(ln n + n))
となります。ここで、r
とw
は、それぞれ合理的な量の靴下に対する環境読み取り操作と環境書き込み操作の要因です。 2つの靴下が同じペアに属しているかどうかを判断するには、一定量の論理演算と算術演算が必要であると想定しているため、論理演算と算術演算のコストは省略されています。これはすべてのシナリオで実行可能ではないかもしれません。
私は、より少ない操作、より少ない時間消費を約束しない別の解決策を考え出しました、しかし、それが巨大な一連の靴下ペアリングにおいてより少ない時間消費を提供するために十分に良い発見的方法でありえるかどうか見るべきです。
前提条件: /同じ靴下があるという保証はありません。それらが同じ色のものであるならば、それはそれらが同じサイズまたはパターンを持つという意味ではありません。靴下はランダムにシャッフルされます。靴下の数が奇数になることがあります(いくつか欠けている、我々はいくつがわからない)。変数 "index"を覚えておき、それを0に設定する準備をします。
結果 1つか2つの山があります。
ヒューリスティック:
また、それらの除去のように、損傷した靴下のチェックを追加することもできます。 2〜3、および13〜14の間に挿入できます。
私はどんな経験や修正についても聞くのを楽しみにしている。
List<Sock> UnSearchedSocks = getAllSocks();
List<Sock> UnMatchedSocks = new list<Sock>();
List<PairOfSocks> PairedSocks = new list<PairOfSocks>();
foreach (Sock newSock in UnsearchedSocks)
{
Sock MatchedSock = null;
foreach(Sock UnmatchedSock in UnmatchedSocks)
{
if (UnmatchedSock.isPairOf(newSock))
{
MatchedSock = UnmatchedSock;
break;
}
}
if (MatchedSock != null)
{
UnmatchedSocks.remove(MatchedSock);
PairedSocks.Add(new PairOfSocks(MatchedSock, NewSock));
}
else
{
UnmatchedSocks.Add(NewSock);
}
}
あなたが靴下を拾うたびに、一箇所にそれを置きます。それからあなたが拾う次の靴下は、それが最初の靴下と一致しない場合は、最初の靴下の横に置きます。もしそうなら、ペアがあります。このようにして、組み合わせがいくつあるかは問題ではなく、選択した靴下ごとに2つの可能性しかありません。それは、すでに靴下の配列に一致しているか、そうでないかのどちらかです。それを配列内の場所に追加します。
これはまた、靴下が一致すると削除されるため、靴下がすべて配列に含まれることはほとんどありません。
靴下を並べ替えるときには、おおよその 基数の並べ替え をして、同じ色/パターンタイプの他の靴下の近くに靴下を落とします。私がその場所で/その近くで完全に一致するのを見ることができる場合を除いて、私はその時点でペアを抽出します。
他のほとんどすべてのアルゴリズム( usrによる最高得点の答え を含む)はソートされ、その後ペアが削除されます。私は人間として、一度に考慮される靴下の数を最小限にすることがより良いことがわかりました。
私はこれを行います:
これは、O(1)時間でのあいまい一致に対する人間の能力を利用しており、これはコンピューティングデバイス上でのハッシュマップの確立といくらか同等です。
最初に特徴的な靴下を引っ張ることで、あなたは最初に、それほど特徴的ではない機能に「ズームイン」するためのスペースを残します。
蛍光色、縞模様の靴下、および3組の長い靴下を排除した後、あなたはおおよそそれらがどれほど着用されているかによって分類されたほとんど白い靴下になるかもしれません。
ある時点で、靴下の違いは他の人が違いに気付かないように十分に小さいので、それ以上のマッチング作業は必要ありません。
実際のものであれ類似のデータ構造であれ、靴下はペアで提供されます。
最も簡単な答えは、ペアを分離できるようにする前に、左右の靴下へのポインタを含むペアの単一のデータ構造を初期化して、靴下を直接またはペアで参照できるようにすることです。靴下は、そのパートナーへのポインタを含むように拡張することもできます。
これは抽象化の層でそれを削除することによって計算ペアリング問題を解決します。
靴下をペアリングするという実用的な問題に同じ考えを適用すると、明らかな答えは次のとおりです。靴下はペアとして提供され、ペアとして(おそらく一緒にボールをつけることによって)ペアとして引き出しに入れられ、ペアとして着用される。しかし、ペアリングを解除することができるのは洗濯機にあるため、必要なのは靴下をまとめて効率的に洗うことができる物理的なメカニズムだけです。
2つの物理的な可能性があります。
各靴下へのポインタを保持する「ペア」オブジェクトの場合は、靴下をまとめるために使用する布の袋を用意できます。これは大規模なオーバーヘッドのようです。
しかし、それぞれの靴下が他の靴下を参照するには、きちんとした解決策があります。ポッパー(アメリカ人の場合は「スナップボタン」)です。
http://www.aliexpress.com/compare/compare-invisible-snap-buttons.html
靴下を脱いで洗濯かごに入れた直後に靴下をはめ込むだけで、靴下をペアの概念を物理的に抽象化する必要がなくなります。
サイズ 'N'のハッシュテーブルを考えます。
正規分布を仮定すると、少なくとも1つの靴下を1つのバケットにマッピングするための「挿入」の推定数はNlogNです(つまり、すべてのバケットがいっぱいです)。
私はこれを別のパズルの一部として導き出したのですが、間違っていることが証明されてうれしいです。 これは私の同じブログ記事です
あなたが持っている靴下のユニークな色の数/パターンの数のおおよその上限に 'N'を対応させましょう。
衝突があったら(a.k.a:match)、単にその靴下を外します。 NlogN靴下の次のバッチで同じ実験を繰り返します。その美しさは、人間の心の働き方のために、NlogN並列比較(衝突解決)をすることができるということです。 :-)
私は自分の努力をO(1)時間のかかるプロセスに減らすために簡単なステップを踏みました。
入力を2種類の靴下(レクリエーション用の白い靴下、仕事用の黒い靴下)のうちの1つに減らすことによって、2つの靴下のうちどちらを持っているかを判断するだけで済みます。 (技術的には、それらが一緒に洗われることは決してないので、私はプロセスをO(0) timeに減らしました)
望ましい靴下を見つけ、あなたの既存の靴下の必要性を排除するのに十分な量で購入するためには、いくらかの事前の努力が必要です。黒い靴下が必要になる前にこれをやったので、私の努力は最小限でしたが、走行距離は変わるかもしれません。
このような事前の取り組みは、非常に人気があり効果的なコードで何度も見られます。例としては、#DEFINEして小数を10進数にすることなどがあります(他の例もありますが、それが今頭に浮かんでくるものです)。
靴下をペアリングし終えたところで、それを実行する最良の方法は次のとおりです。
最悪の場合、それはあなたがn/2の異なるバケツを持つことになり、そしてあなたはどのバケツが現在の靴下のペアを含んでいるかについてn-2の決定をすることを意味する。明らかに、このアルゴリズムはあなたがほんの数ペアしか持っていなければうまくいきます。私は12ペアでそれをやった。
それほど科学的ではありませんが、うまくいきます:)
「移動」操作がかなり高価で、「比較」操作が安価で、検索を元のストレージよりもはるかに高速なバッファにセット全体を移動する必要がある場合は、ソートを必須のものに統合するだけです。移動します。
選別のプロセスを吊り下げて乾いたものにまとめるのは簡単だと思いました。私はとにかくそれぞれの靴下を拾い上げる必要があります、そしてそれを吊るす(移動する)そしてそれは弦の上の特定の場所にそれを吊るすために何も費用がかかりません。バッファ全体(文字列)を強制的に検索するのではなく、色や色合いで靴下を配置することにしました。それぞれの靴下を吊るす前に、一致するものがすでにある場合は「正しい付近」を見ます - これは「スキャン」を2-3の他の靴下に制限します - そしてそれがそうである場合、私はそれのすぐ隣にもう片方をぶら下げます。それから乾いたとき、私はそれらを紐から取り除きながらペアにします。
今やこれはトップアンサーが提案する「色による杭の形成」とそれほど変わらないように思えるかもしれませんが、最初はばらばらの杭を選ぶのではなく、範囲を選びます。それはちょうど間に入ります。そして2つの操作を統合することで(ハングアップとソート)、ハング中のソートのオーバーヘッドは別々のソートの10%になります。
私はこの問題に新しい何かを貢献できることを願っています。私は、すべての答えがあなたがあなたの全体的な洗濯パフォーマンスを遅くすることなく、あなたが 前処理 を実行することができる2つのポイントがあるという事実を無視することに気づきました。
また、大家族の場合でも、大量の靴下を着用する必要はありません。靴下は引き出しから取り出されて着用され、洗濯される前にいる場所(たぶんビン)に投げ込まれます。私は言ったbinをLIFO-Stackとは呼ばないでしょうが、私はそれを仮定するのが安全であると思います
私が知っているすべての洗濯機は(あなたが何個の靴下を洗わなければならないかにかかわらず)サイズが限られており、実際のランダム化は洗濯機で起こります。シングルトン。
私たちの2つの前処理段階は、きれいなだけでなく乾燥した靴下を得るためにしなければならない「洗濯物を靴下に入れる」と「靴下を洗濯物から取り出す」です。洗濯機と同じように、物干しは有限であり、私たちは靴下を見えるようにするために私たちが線の全部を持っていると思います。
これがput_socks_on_line()のアルゴリズムです。
while (socks left in basket) {
take_sock();
if (cluster of similar socks is present) {
Add sock to cluster (if possible, next to the matching pair)
} else {
Hang it somewhere on the line, this is now a new cluster of similar-looking socks.
Leave enough space around this sock to add other socks later on
}
}
靴下を移動したり、最適なものを探したりするために時間を無駄にしないでください。これはすべてO(n)で行う必要があります。靴下はまだ対になっていません、我々はライン上にいくつかの類似性クラスターを持っているだけです。ソックスのセットが限られていると便利です。これは、「良い」クラスターを作成するのに役立つからです(たとえば、ソックスのセットに黒いソックスしかない場合は、色によるクラスター化は不可能です)。
これがtake_socks_from_line()のアルゴリズムです。
while(socks left on line) {
take_next_sock();
if (matching pair visible on line or in basket) {
Take it as well, pair 'em and put 'em away
} else {
put the sock in the basket
}
残りのステップの速度を向上させるためには、次の靴下を無作為に選ぶのではなく、各クラスタから靴下ごとに靴下を順番に取ることが賢明です。どちらの前処理ステップでも、靴下をラインやバスケットに入れるだけで済むので、時間をかけずに済みます。これは、何に関係なくしなければならないため、これによって洗濯物のパフォーマンスが大幅に向上します。
この後、ハッシュ分割アルゴリズムを実行するのは簡単です。通常、約75%の靴下はすでにペアになっていて、非常に小さな靴下のサブセットが残っています。このサブセットはすでに(多少)クラスタ化されています(前処理の後、バスケットにエントロピーをあまり入れません)。もう1つのことは、残りのクラスターは一度に処理できるほど小さいため、バスケットからクラスター全体を取り出すことが可能であるということです。
これがsort_remaining_clusters()のアルゴリズムです。
while(clusters present in basket) {
Take out the cluster and spread it
Process it immediately
Leave remaining socks where they are
}
その後、いくつかの靴下が残っています。これは私が以前にペアになっていない靴下をシステムに導入し、特別なアルゴリズムなしで残りの靴下を処理するところです - 残りの靴下は非常に少なく、視覚的に非常に速く処理することができます。
残りのすべての靴下について、私は彼らの対応するものがまだ洗い流されていないと仮定し、それらを次の反復のために片付けます。あなたが時間の経過とともに不対の靴下の成長を登録するならば(「靴下の漏れ」)、あなたはあなたのビンをチェックするべきです - それはランダム化されるかもしれません(あなたはそこに眠る猫を飼っていますか?)
ある種のLIFOスタックとして機能するビン、制限された通常の洗濯機、制限された通常の洗濯物など、これらのアルゴリズムには多くの前提があることを私は知っています。靴下.
並列処理について:両方の靴下を同じビンに入れている限り、これらすべてのステップを簡単に並列化できます。
前処理をするのはどうですか?すべての靴下にマークまたはID番号をステッチして、すべてのペアが同じマーク/ ID番号を持つようにします。あなたが新しい靴下を買うたびにこのプロセスが行われるかもしれません。その後、O(n)総コストを得るために 基数ソート を実行できます。すべてのマーク/ ID番号の場所を見つけて、ただ一つずつすべての靴下を選んで、それらを正しい場所に入れてください。
それが正式にO(n)
"余分な"スペースを必要とするので、私の解決策はあなたの要求に正確に対応していません。しかし、私の条件を考えると、それは私の実際のアプリケーションでは非常に効率的です。したがって、私はそれが面白いはずだと思います。
私の場合の特別な条件は、私は乾燥機を使用しないことです、ただ私の布を普通の布乾燥機に掛けることです。布をぶら下げるにはO(n)
操作が必要です(ちなみにここでは常に binパッキング problemを考慮しています)。その性質上問題は線形の "余分な"スペースを必要とします。私はバケツから新しい靴下を取るとき、私はペアがすでにハングしている場合は、そのペアの横にそれをハングアップしようとします。それが新しいペアからの靴下であるならば、私はそれの隣にいくらかのスペースを残します。
一致する靴下がすでにどこかにぶら下がっているかどうかを確認するためには、余分な作業が必要になります。これは、コンピュータの場合、ソリューションO(n^2)
を係数1/2
でレンダリングすることになります。しかし、この場合、「ヒューマンファクタ」が実際には利点です。ハングアップしていれば、通常、すぐに(ほとんどO(1)
)マッチングソックスを識別できます(おそらく、認識できないほどの頭脳内キャッシングが関係します)。 Oracle Machine ;-)のように制限された "Oracle"の場合我々は、人間はデジタルマシンよりもこれらの利点を持っている場合があります;-)
O(n)
!したがって、靴下のペアリングの問題と布の吊り下げの問題とを結び付けるには、O(n)
"空きスペース"を無料で手に入れ、O(n)
に間に合うだけの解決策が必要です。月曜日の非常に悪い朝でも靴下のペア... ;-)
ハッシュとしてパターンを使用して、一致しない靴下に使用されるハッシュテーブルを作成します。靴下を一つずつ繰り返します。靴下のハッシュテーブルでパターンが一致する場合は、靴下をテーブルから取り出してペアにします。靴下が一致しない場合は、それをテーブルに入れます。
あなたのn組の靴下をソートする問題はO(n) です。洗濯物を投げる前に basket の前に、左側のものを右側のものに通します。それらを取り出すとき、あなたは糸を切ってあなたの引き出しにそれぞれの組を入れました - n組の2つの操作、それでO(n)。
今度は次の質問は単にあなたがあなた自身の洗濯をし、あなたの妻が彼女をするかどうかということです。これは、 問題の完全に異なるドメイン にある可能性が高い問題です。 :)
私は博士号取得中(コンピュータサイエンス)に非常によく考えました。靴下を見分けることができる能力に応じて複数の解決策を思いついたので、できるだけ早く正しいペアを見つけることができました。
靴下を見て、 それらの独特のパターンを記憶するコストがごくわずかであると仮定します (ε)。それなら最善の解決策は単にテーブルの上にすべての靴下を投げることです。これはそれらのステップを含みます:
これは確かに最速の可能性であり、n + 1 = O(n)複雑度で実行されます。しかし、それはあなたが完全にすべてのパターンを覚えていると仮定しています...実際には、これは事実ではありません、そして私の個人的な経験はあなたが時々マッチするペアを最初の試みで見つけないということです:
これは、一致するペアを見つける能力にかかっています。あなたがダーク/グレーのペアや白いスポーツソックスを持っていて、それらがよく似たパターンを持っている場合、これは特に当てはまります!対応する靴下を見つける確率がPであることを認めましょう。あなたは、ペアを形成するために対応する靴下を見つける前に、平均して1/Pを試す必要があります。全体の複雑度は1 +(n/2)*(1 + 1/P)= O(n)です。
どちらも靴下の数は直線的で、非常によく似た解決方法です。問題を少し修正して、 類似した靴下の複数のペア がセット内にあることを認めましょう。 複数のペアの靴下を一度に格納するのは簡単です (1 +ε)。 K個の異なるパターンの場合は、次のように実装できます。
全体の複雑度は、n + K = O(n)になります。それはまだ線形ですが、正しいアルゴリズムを選ぶことは今PとKの値に大きく依存するかもしれません!しかし、靴下ごとにクラスタを見つける(または作成する)のが難しい場合があることに、もう1人の人が反対するかもしれません。
そのうえ、あなたはウェブサイトで最も良いアルゴリズムが何であるかを見て、あなた自身の解決策を提案することによって時間をゆるめるかもしれません:)
ジョークモードオン
申し訳ありませんが、皆さんは明らかに間違っています。靴下を使用した実際のユースケースでは、O(1)を使用することで完璧な靴下のマッチングを実現できます。
これら を1袋購入するだけで、靴下を足から引き出して洗濯機から出てきたときに靴下を毎日装着し、魔法のようにペアになっています。
(私はこの売り手とは何の関係もありません。多くのものからこのものを見つけることができます)
ジョークモードオフ
2行の考え方、つまり、一致を見つけるのにかかる速度と、ストレージと比較してすべての一致を見つけるのにかかる速度との対比。
2番目のケースでは、ソックスにすべての試合を問い合わせるGPU並列バージョンを指摘したいと思いました。
一致させるプロパティが複数ある場合は、単純なものにするために、単純なGPUベースのクエリですが、グループ化されたタプルとより見栄えのするZip反復子、および推力の変換関数を使用できます。
//test.cu
#include <thrust/device_vector.h>
#include <thrust/sequence.h>
#include <thrust/copy.h>
#include <thrust/count.h>
#include <thrust/remove.h>
#include <thrust/random.h>
#include <iostream>
#include <iterator>
#include <string>
// Define some types for pseudo code readability
typedef thrust::device_vector<int> GpuList;
typedef GpuList::iterator GpuListIterator;
template <typename T>
struct ColoredSockQuery : public thrust::unary_function<T,bool>
{
ColoredSockQuery( int colorToSearch )
{ SockColor = colorToSearch; }
int SockColor;
__Host__ __device__
bool operator()(T x)
{
return x == SockColor;
}
};
struct GenerateRandomSockColor
{
float lowBounds, highBounds;
__Host__ __device__
GenerateRandomSockColor(int _a= 0, int _b= 1) : lowBounds(_a), highBounds(_b) {};
__Host__ __device__
int operator()(const unsigned int n) const
{
thrust::default_random_engine rng;
thrust::uniform_real_distribution<float> dist(lowBounds, highBounds);
rng.discard(n);
return dist(rng);
}
};
template <typename GpuListIterator>
void PrintSocks(const std::string& name, GpuListIterator first, GpuListIterator last)
{
typedef typename std::iterator_traits<GpuListIterator>::value_type T;
std::cout << name << ": ";
thrust::copy(first, last, std::ostream_iterator<T>(std::cout, " "));
std::cout << "\n";
}
int main()
{
int numberOfSocks = 10000000;
GpuList socks(numberOfSocks);
thrust::transform(thrust::make_counting_iterator(0),
thrust::make_counting_iterator(numberOfSocks),
socks.begin(),
GenerateRandomSockColor(0, 200));
clock_t start = clock();
GpuList sortedSocks(socks.size());
GpuListIterator lastSortedSock = thrust::copy_if(socks.begin(),
socks.end(),
sortedSocks.begin(),
ColoredSockQuery<int>(2));
clock_t stop = clock();
PrintSocks("Sorted Socks: ", sortedSocks.begin(), lastSortedSock);
double elapsed = (double)(stop - start) * 1000.0 / CLOCKS_PER_SEC;
std::cout << "Time elapsed in ms: " << elapsed << "\n";
return 0;
}
//nvcc -std=c++11 -o test test.cu
1000万ソックスの実行時間:9ミリ秒
前提条件
アルゴリズム
試してください:
を除いて:
テーブルの大きさが足りない:
対応していない靴下をすべて慎重に混ぜ合わせてから、操作を再開します
//この操作は新しいパイルと空のテーブルになります
テーブルの上に靴下が残っていない:
throw(最後の修復不可能な靴下)
靴下は山に残っていません:
洗濯室を出る
最後に:
既知の問題
周りにテーブルがない場合、またはテーブル上に少なくとも1つの靴下を収容するのに十分な場所がない場合、アルゴリズムは無限ループに入ります。
考えられる改善
ソートするソックスの数に応じて、十分なスペースがある場合は、ソートすることでスループットが向上する可能性がありますソックス。
これが機能するためには、靴下の各ペアに固有の値を持つ属性が必要です。そのような属性は靴下の視覚的特性から容易に合成することができる。
上記の属性でテーブルの靴下を並べ替えます。その属性を 'color'と呼びましょう。靴下を一列に並べ、暗い色の靴下を右側(Push_back())に、明るい色の靴下を左側(Push_front())に配置します。
巨大な杭、特にこれまで目に見えなかった靴下では、属性合成にかなりの時間がかかる可能性があるため、スループットは明らかに低下します。ただし、これらの属性はメモリに保存して再利用することができます。
この可能性のある改善の効率を評価するためにはいくつかの研究が必要です。以下の質問が発生します。
_ mcve _ のガイドラインに沿ったPoC:
#include <iostream>
#include <vector>
#include <string>
#include <time.h>
using namespace std;
struct pileOfsocks {
pileOfsocks(int pairCount = 42) :
elemCount(pairCount<<1) {
srand(time(NULL));
socks.resize(elemCount);
vector<int> used_colors;
vector<int> used_indices;
auto getOne = [](vector<int>& v, int c) {
int r;
do {
r = Rand() % c;
} while (find(v.begin(), v.end(), r) != v.end());
v.Push_back(r);
return r;
};
for (auto i = 0; i < pairCount; i++) {
auto sock_color = getOne(used_colors, INT_MAX);
socks[getOne(used_indices, elemCount)] = sock_color;
socks[getOne(used_indices, elemCount)] = sock_color;
}
}
void show(const string& Prompt) {
cout << Prompt << ":" << endl;
for (auto i = 0; i < socks.size(); i++){
cout << socks[i] << " ";
}
cout << endl;
}
void pair() {
for (auto i = 0; i < socks.size(); i++) {
std::vector<int>::iterator it = find(unpaired_socks.begin(), unpaired_socks.end(), socks[i]);
if (it != unpaired_socks.end()) {
unpaired_socks.erase(it);
paired_socks.Push_back(socks[i]);
paired_socks.Push_back(socks[i]);
}
else
unpaired_socks.Push_back(socks[i]);
}
socks = paired_socks;
paired_socks.clear();
}
private:
int elemCount;
vector<int> socks;
vector<int> unpaired_socks;
vector<int> paired_socks;
};
int main() {
pileOfsocks socks;
socks.show("unpaired socks");
socks.pair();
socks.show("paired socks");
system("pause");
return 0;
}
私の提案する解決策は、 color を除いて、すべての靴下の詳細が同じであると仮定しています。靴下の間に延期する詳細がある場合は、これらの詳細を使用して、私の例では色の代わりにさまざまな種類の靴下を定義できます。
靴下がたくさんあることを考えれば、靴下は青、赤、または緑の3色になります。
それから、各色に対して parallel workerを作成できます。対応する色を塗りつぶすための独自のリストがあります。
At time i:
Blue read Pile[i] : If Blue then Blue.Count++ ; B=TRUE ; sync
Red read Pile[i+1] : If Red then Red.Count++ ; R=TRUE ; sync
Green read Pile [i+2] : If Green then Green.Count++ ; G=TRUE ; sync
同期処理あり
Sync i:
i++
If R is TRUE:
i++
If G is TRUE:
i++
これには初期化が必要です。
Init:
If Pile[0] != Blue:
If Pile[0] = Red : Red.Count++
Else if Pile[0] = Green : Green.Count++
If Pile[1] != Red:
If Pile[0] = Green : Green.Count++
どこで
Best Case: B, R, G, B, R, G, .., B, R, G
Worst Case: B, B, B, .., B
Time(Worst-Case) = C * n ~ O(n)
Time(Best-Case) = C * (n/k) ~ O(n/k)
n: number of sock pairs
k: number of colors
C: sync overhead