当我们应该使用快速排序时,我试图理解一个公式。例如,我们有一个包含 N = 1_000_000 元素的数组。如果只搜索一次 ,则应该使用简单的线性搜索,但是如果要搜索10次,则应该使用排序数组 O(n log n)。我应该如何检测阈值的输入阈值以及何时使用排序的输入数组,然后使用二进制搜索?
答案 0 :(得分:3)
您要解决的不平等问题可能被形容为
t * n > C * n * log(n) + t * log(n)
其中t
是检查次数,而C
是用于排序实现的常数(应通过实验确定)。评估此常数时,可以用数值方法解决不等式(当然有不确定性)
答案 1 :(得分:2)
就像您已经指出的那样,这取决于您要执行的搜索次数。以下语句可以得出一个很好的阈值:
n*log[b](n) + x*log[2](n) <= x*n/2
x是搜索次数; n输入大小; b排序的对数底数,具体取决于您使用的分区。
当该语句的值为真时,应将方法从线性搜索切换为排序和搜索。
通常来说,通过无序数组进行线性搜索平均需要执行n / 2步,尽管只有x接近n时,该平均值才发挥重要作用。如果您要坚持使用大Omicron或大Theta表示法,则可以忽略上面的/2
。
答案 2 :(得分:2)
假设n
个元素和m
个搜索,具有粗略的近似值
排序费用为C0.n.log n
,
m
二进制搜索C1.m.log n
的费用,
m
线性搜索C2.m.n
的费用,
带有C2 ~ C1 < C0
。
现在比较
C0.n.log n + C1.m.log n vs. C2.m.n
或
C0.n.log n / (C2.n - C1.log n) vs. m
对于相当大的n
,收支平衡点约为C0.log n / C2
。
例如,以C0 / C2 = 5
,n = 1000000
得出m = 100
。
答案 3 :(得分:1)
您应该绘制两个操作的复杂性。
线性搜索:O(n)
排序和二进制搜索:O(nlogn + logn)
在图中,您将看到选择一种方法优于另一种方法有意义的n
。
答案 4 :(得分:1)
当我查看类似quicksort的算法的预期运行时间(每个级别的预期分割不是50/50时)时,这实际上变成了一个有趣的问题。
我要回答的第一个问题是随机数据,每个级别的平均拆分是多少。当然,它必须大于50%(对于较大的细分)。好吧,给定一个大小为N的随机值数组,最小值具有(1,N-1)的细分,第二个最小值具有(2,N-2)的细分,依此类推。我将其放在快速脚本:
#models.py
from django.db import models
from datetime import datetime
# Create your models here.
class Post(models.Model):
title = models.CharField(max_length=200)
body = models.TextField()
created_at = models.DateTimeField(default=datetime.now, blank=True)
image = models.ImageField(upload_to='blog/', null=True, blank=True)
def __str__(self):
return self.title
#view.py
from django.shortcuts import render
from django.http import HttpResponse
from .models import Post
# Create your views here.
def index(request):
posts = Post.objects.all()[:10]
context = {
'title' : 'Latest Posts',
'posts' : posts,
}
return render(request, 'blog/index.html', context)
def details(request, id):
post = Post.objects.get(id=id)
queryset = Post.objects.all()
context = {
'post' : post,
'object' : queryset
}
return render(request, 'blog/details.html', context)
#details.html
{% extends 'blog/layout.html'%}
{% block content %}
<h3 class="center-align red darken-3">{{post.title}}</h3>
<div class="card hoverable">
<div class="card-content teal accent-4">
{{post.body}}
</div>
<div class="card-action teal accent-4">
{{post.created_at}}
</div>
{% if post.image.url %}
{% for post_image in post.image.url %}
<div class="div">
<img src='{{post.image.url}}' class='responsive-img' /><br/>
</div>
{% else %}
<div class="div">
</div>
{% endfor %}
{% endif %}
</div>
<a href="/blog/" class="btn">Go Back</a>
{% endblock %}
答案是0.75。我敢肯定,我可以证明这始终是正确的答案,但我想继续讲更难的部分。
现在,我们假设对于一些未知的对数底数,即使25/75分割也遵循nlogn进行。这意味着split = 0
for x in range(10000):
split += float(max(x, 10000 - x)) / 10000
split /= 10000
print split
的问题是要通过统计手段找到num_comparisons(n) = n * log_b(n)
(因为我不希望该模型在每个步骤上都是精确的)。在使用对数标识获得以下信息后,我们可以巧妙地应用最小二乘拟合:
b
现在对数可以有任何底数,只要C(n) = n * log(n) / log(b)
和log(n)
使用相同的底数即可。这是一个线性方程,仅在等待一些数据!因此,我编写了另一个脚本来生成log(b)
的数组,并用xs
和C(n)
填充并用ys
填充,并使用n*log(n)
告诉我最小二乘拟合的斜率,我希望等于numpy
。我运行脚本,并将1 / log(b)
放入b
内,具体取决于我将[2.16, 2.3]
设置为多高(我将n从100变为100'000'000)。 n
似乎因b
而异的事实表明我的模型并不精确,但我认为此示例可以。
现在要根据这些假设实际回答您的问题,我们可以求解以下时间的临界点:n
。我只是假设二进制搜索的对数底数与25/75拆分的排序方法相同。隔离N * n/2 = n*log_2.3(n) + N * log_2.3(n)
会得到:
N
如果您的搜索次数N = n*log_2.3(n) / (n/2 - log_2.3(n))
超过RHS的数量(其中N
是所讨论的数组的大小),则对它进行一次排序并对其进行二进制搜索会更有效。