O(1 / n)アルゴリズムはありますか?
または、O(1)よりも小さいものは何ですか?
O(1 / n)アルゴリズムはありますか?
または、O(1)よりも小さいものは何ですか?
回答:
この質問は、見かけほど馬鹿ではありません。少なくとも理論的には、Big O表記の数学的定義をとると、O(1 / n)などは完全に賢明です。
これで、簡単にg(x)を1 / xに置き換えることができます。上記の定義が一部のfにも当てはまることは明らかです。
漸近的な実行時の増加を推定する目的では、これは実行可能性が低くなります。意味のあるアルゴリズムは、入力が増加するにつれて速くなりません。もちろん、これを実現する任意のアルゴリズムを構築できます。たとえば、次のようなものです。
def get_faster(list):
how_long = (1 / len(list)) * 100000
sleep(how_long)
明らかに、この関数は、入力サイズが大きくなるにつれて、ハードウェアによって強制される少なくとも数回の制限(数値の精度、sleep
待機できる最小時間、引数の処理時間など)までに費やす時間が短くなります。この制限は、定数の下限なので、実際には上記の関数にはまだランタイムO(1)があります。
しかし、そこにある入力のサイズが大きくランタイムは(少なくとも部分的に)減少させることができ、実際、現実世界のアルゴリズムに。ただし、これらのアルゴリズムはO(1)以下では実行時の動作を示さないことに注意してください。それでも、彼らは興味深いです。たとえば、Horspoolによる非常に単純なテキスト検索アルゴリズムを見てみましょう。ここで、予想される実行時間は、検索パターンの長さが増加するにつれて減少します(ただし、干し草の山の長さが増加すると、実行時間が再び増加します)。
はい。
実行時O(1 / n)には、「空の」アルゴリズムという正確に1つのアルゴリズムがあります。
アルゴリズムがO(1 / n)であることは、単一の命令で構成されるアルゴリズムよりも少ないステップで漸近的に実行されることを意味します。すべてのn> n0について、1ステップ未満で実行する場合は、それらのnに対してまったく命令を含まないようにする必要があります。「if n> n0」のチェックには少なくとも1つの命令がかかるため、すべてのnに対して命令を含めないでください。
まとめ:O(1 / n)である唯一のアルゴリズムは、命令なしで構成される空のアルゴリズムです。
sharptoothは正しく、O(1)が可能な限り最高のパフォーマンスです。ただし、これは高速ソリューションを意味するのではなく、固定時間ソリューションを意味します。
興味深い変種、そしておそらく実際に提案されているのは、人口が増えるにつれて問題が簡単になることです。私は1と考えることができます。
セット内の2人の人の誕生日が同じですか?nが365を超える場合、trueを返します。365未満の場合でも、これはO(n ln n)です。問題は徐々に簡単になるわけではなく、n> 365の場合はO(1)になるため、おそらく良い答えではありません。
それは可能ではありません。ビッグOの定義は以下の不等式:
A(n) = O(B(n))
<=>
exists constants C and n0, C > 0, n0 > 0 such that
for all n > n0, A(n) <= C * B(n)
したがって、B(n)は実際には最大値であるため、nが増加するにつれてB(n)が減少しても、推定値は変化しません。
以前の大きなO表記の学習から、1つのステップ(変数のチェック、代入の実行など)が必要な場合でも、それはO(1)です。
「定数」は関係ないため、O(1)はO(6)と同じであることに注意してください。そのため、O(n)はO(3n)と同じです。
したがって、1ステップも必要な場合はO(1)です。プログラムでは少なくとも1ステップが必要なので、アルゴリズムが実行できる最小値はO(1)です。私たちがそれを行わない限り、それはO(0)だと思いますか?何もしなければ、それはO(1)であり、それが最低限必要なことです。
(私たちがそれをしないことを選択した場合、それはZenまたはTaoの質問になる可能性があります...プログラミングの領域では、O(1)は依然として最小です)。
またはこれはどうですか:
プログラマー:ボス、O(1)時間でそれを行う方法を見つけました!
ボス:それをする必要はありません、私たちは今朝破産しました。
プログラマー:ああ、それでO(0)になります。
関数をまったく実行しない(NOOP)のはどうですか?または固定値を使用します。それは重要ですか?
私はO(1 / n)を使用して、入力が大きくなるにつれて小さくなる確率を記述します-たとえば、公正なコインがlog2(n)フリップでテールになる確率はO(1 / n)です。
この質問を読んでいて、会話が何であるかを理解したい人にとって、これは役立つかもしれません:
| |constant |logarithmic |linear| N-log-N |quadratic| cubic | exponential |
| n | O(1) | O(log n) | O(n) |O(n log n)| O(n^2) | O(n^3) | O(2^n) |
| 1 | 1 | 1 | 1| 1| 1| 1 | 2 |
| 2 | 1 | 1 | 2| 2| 4| 8 | 4 |
| 4 | 1 | 2 | 4| 8| 16| 64 | 16 |
| 8 | 1 | 3 | 8| 24| 64| 512 | 256 |
| 16 | 1 | 4 | 16| 64| 256| 4,096 | 65536 |
| 32 | 1 | 5 | 32| 160| 1,024| 32,768 | 4,294,967,296 |
| 64 | 1 | 6 | 64| 384| 4,069| 262,144 | 1.8 x 10^19 |
量子アルゴリズムは重ね合わせによって「一度に」複数の計算を実行できると思います...
これは有用な答えではないかと思います。
多くの人が正しい答えを持っています(いいえ)これを証明する別の方法を次に示します。関数を使用するには、関数を呼び出し、答えを返す必要があります。これには一定の時間がかかります。大規模な入力の場合、残りの処理にかかる時間が短くても、回答(1ビットであると想定できます)の出力には少なくとも一定の時間がかかります。
人口が増えるにつれて、どの問題が簡単になりますか?1つの答えは、ダウンロード速度がノード数の逆関数であるビットトレントのようなものです。ロードするほど速度が低下する車とは異なり、bittorrentのようなファイル共有ネットワークは、接続するノードの数を増やします。
O(1)を下回ることはできませんが、kがNより小さいO(k)は可能です。それらをサブリニア時間アルゴリズムと呼びました。一部の問題では、サブリニア時間アルゴリズムは特定の問題の近似解しか提供できません。ただし、データセットが大きすぎるか、すべてを計算するには計算コストが高すぎるために、近似解が適切な場合もあります。
これはどうですか:
void FindRandomInList(list l)
{
while(1)
{
int rand = Random.next();
if (l.contains(rand))
return;
}
}
リストのサイズが大きくなると、プログラムの予想される実行時間が減少します。
constains
O(1)を使用
O(1 / n)はO(1)よりも小さくありません。つまり、基本的には、データが多いほど、アルゴリズムが高速になります。たとえば、配列を取得し、それよりも少ない場合は常に最大10 100の要素を入力し、それ以上の場合は何もしないとします。これはもちろんO(1 / n)ではなく、O(-n)のようなものです:) O-bigの表記が小さすぎると、負の値は許可されません。
指摘したように、null関数の例外の可能性があることを除いO(1/n)
て、所要時間が0に近づく必要があるため、関数がない場合があります。
もちろん、Konradによって定義されたようなアルゴリズムがいくつかありますが、それらはO(1)
少なくともある意味では少なくなるはずです。
def get_faster(list):
how_long = 1/len(list)
sleep(how_long)
これらのアルゴリズムを調査する場合は、独自の漸近測定または独自の時間の概念を定義する必要があります。たとえば、上記のアルゴリズムでは、いくつかの「無料」操作を設定された回数だけ使用できるようにすることができます。上記のアルゴリズムで、睡眠以外のすべての時間を除外してt 'を定義すると、t' = 1 / n、つまりO(1 / n)になります。漸近的な振る舞いはささいなことなので、おそらくより良い例があります。事実、誰かが重要な結果をもたらす感覚を思い付くことができると私は確信しています。
残りの回答のほとんどは、big-Oがアルゴリズムの実行時間のみに関するものであると解釈しています。しかし、質問にはそれが言及されていなかったので、数値分析におけるbig-Oの他のアプリケーション、つまりエラーについて言及する価値があると思いました。
多くのアルゴリズムは、ステップサイズ(h)と分割数(n)のどちらを話しているかに応じて、O(h ^ p)またはO(n ^ {-p})になります。たとえば、オイラー法では、y(0)とdy / dx(yの導関数)がわかっていれば、y(h)の推定値を探します。y(h)の推定値は、hが0に近いほど正確です。したがって、任意のxのy(x)を見つけるには、0からxまでの間隔をとり、n個になるまで分割し、オイラー法を実行します。各ポイントで、y(0)からy(x / n)からy(2x / n)などに取得します。
したがって、オイラー法はO(h)またはO(1 / n)アルゴリズムになります。ここで、hは通常ステップサイズとして解釈され、nは区間を分割する回数として解釈されます。
また、浮動小数点の丸め誤差のため、実際の数値解析アプリケーションでO(1 / h)を使用することもできます。間隔を小さくすると、特定のアルゴリズムの実装で発生するキャンセルが多くなり、有効数字の損失が増えるため、エラーがアルゴリズムを通じて伝搬されます。
オイラー法の場合、浮動小数点を使用している場合は、十分に小さなステップとキャンセルを使用して、大きな数に小さな数を追加し、大きな数は変更しないままにします。滑らかな関数yで、y '(x)を(y(x + h)-y(x)/ h)で近似して、2つの非常に近い位置で評価された関数から互いに2つの数値を減算して導関数を計算するアルゴリズムの場合(x + h)はy(x)に近づき、大きなキャンセルとより少ない有効桁数での導関数の推定をもたらします。これは、派生物を必要とするアルゴリズム(たとえば、境界値問題)に伝播します。
OK、私はそれについて少し考えました、そしておそらくこの一般的な形式に従うことができるアルゴリズムが存在します:
1000ノードグラフの巡回セールスマン問題を計算する必要がありますが、訪問できないノードのリストも与えられます。訪問できないノードのリストが大きくなるにつれて、問題の解決が容易になります。
確かに上限にO(1 / n)であるアルゴリズムが表示されます。
ルーチンの外部の何かが原因で変化している一連の大量の入力があり(ハードウェアを反映しているか、それを実行しているプロセッサの他のコアである可能性もあります)、ランダムで有効なものを選択する必要があります。
さて、それが変化していなければ、アイテムのリストを作成し、ランダムに1つ選択してO(1)時間を取得します。ただし、データの動的な性質により、リストを作成することはできません。単にランダムにプローブして、プローブの有効性をテストする必要があります。(そして、本質的に返答が返されたときに答えがまだ有効である保証はありません。これにはまだ用途がある可能性があります。たとえば、ゲーム内のユニットのAIです。トリガーを引く。)
これは、ワーストケースのパフォーマンスが無限大ですが、データスペースがいっぱいになると、平均ケースのパフォーマンスが低下します。
数値解析では、近似アルゴリズムは近似許容誤差において準一定の漸近的な複雑さを持っている必要があります。
class Function
{
public double[] ApproximateSolution(double tolerance)
{
// if this isn't sub-constant on the parameter, it's rather useless
}
}
O(1)未満は不可能だと思います。アルゴが取った時間はすべてO(1)と呼ばれます。しかし、O(1 / n)の場合、以下の関数はどうでしょう。(私はこのソリューションですでに提示された多くのバリアントがあることを知っていますが、それらはすべていくつかの欠陥があると思います(メジャーではなく、それらはコンセプトをよく説明しています)。
def 1_by_n(n, C = 10): #n could be float. C could be any positive number
if n <= 0.0: #If input is actually 0, infinite loop.
while True:
sleep(1) #or pass
return #This line is not needed and is unreachable
delta = 0.0001
itr = delta
while delta < C/n:
itr += delta
したがって、nが増加するにつれて、関数の所要時間はますます短くなります。また、入力が実際に0の場合、関数が戻るまでに時間がかかることが保証されます。
機械の精度によって制限されると主張する人もいるでしょう。したがって、sinc eitには上限があり、O(1)です。しかし、文字列でnとCの入力を取得することで、これをバイパスすることもできます。そして、追加と比較は文字列で行われます。これにより、nを任意に小さくすることができます。したがって、n = 0を無視しても、関数の上限は制限されません。
また、ランタイムがO(1 / n)であるとは言えないと私は信じています。しかし、O(1 + 1 / n)のようなものを言う必要があります
O(1 / n)であるアルゴリズムを構築することは可能かもしれません。1つの例は、f(n)がnよりも大きいことが保証されている関数であるf(n)-nの倍数を反復するループであり、nが無限大に近づくときのf(n)-nの制限は、ゼロ。f(n)の計算も、すべてのnに対して一定である必要があります。私はf(n)がどのように見えるのか、またはそのようなアルゴリズムがどのようなアプリケーションを持っているのかわからないが、私の意見ではそのような関数が存在する可能性はあるが、結果のアルゴリズムはアルゴリズムの可能性を証明する以外に目的はないO(1 / n)。
Big-O表記は、アルゴリズムの最悪のシナリオを表しています。これは、通常のランタイムとは異なります。O(1 / n)アルゴリズムがO(1)アルゴリズムであることを証明するのは簡単です。定義により、
O(1 / n)-> T(n)<= 1 / n、すべてのn> = C> 0に対して
O(1 / n)-> T(n)<= 1 / C、 1 / n <= 1 / C for all n> = C
O(1 / n)-> O(1)、Big-O表記は定数を無視するため(つまり、Cの値は関係ありません)
hashtable-contains
O(1)として表すことができるアルゴリズムの予想される(および平均の)実行時間を考慮してください。最悪の場合は、Theta(n)として非常に正確に指定できます。オメガとシータは単に他の境界を示すために使用されるかもしれませんが、もう一度言います。それらは平均または最良のケースとは何の関係もありません。
O(1)よりも小さいものはありませんBig-O表記は、アルゴリズムの最大の複雑さを意味します
アルゴリズムの実行時間がn ^ 3 + n ^ 2 + n + 5の場合、それはO(n ^ 3)です。n-> Infであるため、n ^ 2は、 n ^ 3
同様に、n-> Infと同様に、O(1 / n)はO(1)と比較して無関係であるため、3 + O(1 / n)はO(1)と同じになるため、O(1)は可能な限り最小の計算になります複雑
inline void O0Algorithm() {}
簡単なO(1 / n)アルゴリズムを次に示します。そしてそれは面白いことさえします!
function foo(list input) {
int m;
double output;
m = (1/ input.size) * max_value;
output = 0;
for (int i = 0; i < m; i++)
output+= random(0,1);
return output;
}
O(1 / n)は、入力のサイズが大きくなると関数の出力がどのように変化するかを説明するため、可能です。関数1 / nを使用して関数が実行する命令の数を記述している場合、関数が任意の入力サイズで命令を0にする必要はありません。むしろ、すべての入力サイズについて、あるしきい値を超えるnの場合、必要な命令の数は、1 / nを掛けた正の定数によって制限されます。1 / nが0である実際の数はなく、定数は正なので、関数が0以下の命令を取るように制約される理由はありません。