如何在C ++排序期间监视/显示进度

时间:2010-06-22 16:17:50

标签: c++ algorithm sorting progress-bar

我打算编写一个交互式C ++几何处理插件,它经常会对大量数据进行排序。虽然初步迹象表明该类型只需要一两秒钟,但我更愿意在此期间显示进度 - 即我想每秒更新一次进度指示器。这比打开等待光标并让用户使用程序冻结不确定的时间长度更好(即使只是几秒钟)。

如果我使用类似std :: sort的东西,我可以使用比较功能来不时更新进度指示器,但我不知道'百分比完成'。我也可以将排序分解为子排序,更新子排序之间的进度,然后合并。我最好的选择可能是编写自己的排序方法,虽然我不知道需要多少努力才能获得与std :: sort一样好的性能(并确保正确性)。在任何情况下,该排序方法偶尔会向回调方法发送“完成百分比”。

我想知道其他人是否已经遇到并解决了这个问题 - 我希望在标准库中有一种排序方法可以做我想要的,或者其他一些我没有想过的技术。

更新:感谢您的回答。有一些非常好的建议,在我有机会在即将开展的项目中测试这些想法之前,我将推迟选择接受的答案。

更新2:我完成了我的项目,结果证明这是一个非问题(至少对于客户而言。由于他们将销售该软件,他们仍可能会收到他们的反馈客户会改变他们的想法)。选择一个接受的答案很难,因为有很多好的答案,但最后我选择的那个指向一个关于Merge Sort的wiki文章,它有一个非常令人回味的动画。所以,如果我需要继续这样做,这是我会追求的第一个策略)。

7 个答案:

答案 0 :(得分:9)

我认为,即使您编写了自己的类型,如果您希望进度指示器准确,则必须进行大量仔细测量。如果您只想要一个近似进度指示器,那么您可以使用某些指标,例如“比较元素之间的平均距离”或“比较数量与快速排序的平均预期数量相比”作为您的指标,并实施您已经提到的比较想法。 / p>

是的,我认为你不是一个完全白痴,并且不打算在每次比较中更新进度指示器。如果你这样做,你将花费更多的时间来表明进展而不是排序。

例如,您通常会期望快速排序的n log2 n次操作。涉及多少比较的分析更详细,并且可以比一般度量更准确,但是出于本示例的目的,我们假设。因此,您可以计算比较并报告number_of_comparisons / (n log2 n)作为您对进度的估算。

由于这只是一个平均指标,我会进行一些实验,看看你的估计有多远,并投入一些软糖因素,使其与平均预期情况一致。你也可以有一个进度条,通过点击“这就是我认为我将要完成的地方”来表明不确定性。指标和指标后的一些空间。

即使您使用自己的排序并提出了更加看似精确的度量,进度条仍然无法顺利更新,效果也会类似。你知道你的排序要花多长时间的唯一方法就是你使用一个稍慢但实际上可预测的排序,在这种情况下,你可以预测从元素数量中需要多长时间,或者使用非常快的在特定情况下具有较少可预测行为的排序,在这种情况下,没有真正的方法来获得完全准确的进度条。

子任务的可预测性和总比较数的可预测性密切相关。所以我真的不认为子任务比总比较数更好。

如果您想使用自己的排序并且可预测性是您的最高目标,请转到heapsort。它仍然是O(n log2 n)排序,它接近于最小比较排序(或者我记得从阅读Knuth)。无论其喂食的数据集如何,它还需要非常可预测的时间来完成。它是较慢的O(n log2 n)排序之一,但仍然是。

正如您提到的一位评论者所说,您可能正在解决实际上并不存在的问题。先运行一些实验。然而,问题是一个有趣的智力挑战,无论其有用性如何。 : - )

答案 1 :(得分:4)

由于std :: sort是基于模板的,因此源应该在标头中可用。您可以复制它并插入进度回调。最大的问题是预测你完成的距离 - 大多数排序函数将基于Quicksort,它并不总是进行相同数量的比较。

编写自己的Merge sort是可能的;算法很简单,步骤数很明确。

答案 2 :(得分:2)

我建议您使用第二个选项:使用std::sort或其他标准排序函数,例如qsort,并让比较器报告其进度。但是不要在每次比较中更新 - 这将是难以忍受慢 - 而是更新每(比如说)100ms。

答案 3 :(得分:1)

我看到你的问题如下:

  1. 您希望在单个连续过程中触发离散事件。
  2. 这个细分只是告诉用户正在进行的事情。
  3. 我的建议是:

    1. 使用http://ajaxload.info/之类的加载图标,或者如果它不是基于gui的环境,只需拼出加载。由于事件不到2秒,这不会是一个问题。如果等待时间超过10秒,预计会挂起。

    2. 编写自己的排序方法会带来许多线程安全问题,如果您的代码使用多线程或将来必然会这样做,可能会导致问题。

    3. 3.另外一些重要的信息,您应该考虑每次要排序时数据的严重错误,因此实际上您将测量存在的随机性程度,以及您可能需要的预期计算次数去做。您可以使用此信息作为指示需要多少交换的指标,这反过来可以在您通过排序进行迭代时计算。玩弄数据。

答案 4 :(得分:1)

使用暴力:)

int elem_num = raw_data.size();
int percentage_delta = 100/(elem_num/20);
int percentage = 0;
int i = 0;
std::multiset<Elem*> sorted_data(&compareElemFunc);
foreach(Elem& elem, raw_data)
{
    sorted_data.insert(&elem);
    if(i%20)
    {
        updateProgressBar(percentage);
        percentage += percentage_delta;
    }
    i++;
}
//now, your data is perfectly sorted, iterate through sorted_data

(如果您不想实现自己的std :: sort(),并且因为我缺乏完整的要求)

答案 5 :(得分:0)

当每个部分完成时,使用observer pattern向父母发出信号。使用它和需要排序的元素总数,您可以实时更新进度条。

答案 6 :(得分:0)

我不建议尝试破解std :: sort。这通常是通过introsort实现的,并且是一种非常快速的NLogN操作。构建要排序的容器通常比排序数据更昂贵。

但是,如果您要实现进度条,我建议您将排序放在单独的线程中。通常,多线程应用程序比单线程应用程序更难编写和维护,但是您可以通过不适用于此进度条的方式来执行此操作。除了此进度条以外,您的应用程序仍然可以主要是单线程而不执行任何并发操作,并且可能需要一些事件处理来保持UI响应。当你准备好对数据进行排序时,只需触发另一个线程来完成它并将主线程置于等待循环中,直到排序线程完成,在这里和那里睡觉并同时升级进度条。

您可以将这种非侵入式方法概括为任何类型的耗时操作,而无需在整个代码中使用update_progress_bar()类型调用或深入研究std :: sort的实现或尝试重新发明轮子。因为主线程将处于等待/更新进度条状态,因此在某种意义上阻塞直到您的工作线程完成,您没有任何与多线程相关的问题(需要线程同步来访问整个您的共享资源)应用程序,除了进度计数器,竞争条件,死锁等)。它也是你可以实现的最顺利的进度计数器,因为它将同时更新。

如果您担心与锁定进度计数器相关的效率,只需使用原子操作来递增它。

至于确定排序算法的进展程度,有几种方法可以做到。一种是让它以你拥有的数据大小运行一次,并尝试预测后续运行所需的时间。这完全是非侵入性的,但有点难以做到,但是,如果做得对,它将比定期递增计数器更准确地监控进度(这省略了间隔可能不需要花费很多时间的事实)。第二种方法更简单但有点恶意是修改比较器谓词以增加进度计数器。使用状态制作谓词通常是不受欢迎的,但它比仅仅因为你想要一个进度计数器而试图实现你自己的introsort更不邪恶。

此外,如果你的入侵时间太长,我不得不怀疑,你的容器是否存储了这些三角形对象或指向它们的指针?如果是前者,你可能想要考虑后者,因为它应该会大大加快速度。

相关问题