【问题标题】:Sort a big number list (100k) as quickly as possible using limited operations [closed]使用有限的操作尽快对大数字列表(100k)进行排序[关闭]
【发布时间】:2017-04-11 00:10:55
【问题描述】:

我有两个列表:列表 A 包含一组随机数,列表 B 为空。我必须对列表 A 进行排序。

我可以对这两个列表进行有限的操作,例如:

  • 将列表 A 或 B 的第一个元素移动到列表 B 或 A 的开头
  • 交换列表 A 或 B 的前两个元素,如 32 41 8 9 变为 41 32 8 9
  • 使列表 A 或 B 的最后一个元素成为此列表中的第一个元素(旋转),如 32 41 8 9 变为 9 32 41 8
  • 使列表 A 或 B 的第一个元素成为此列表中的最后一个元素(轮换),如 32 41 8 9 变为 41 8 9 32

我已经设置了一个算法来使用列表 B 作为堆栈和允许的操作集对列表 A 进行排序,但是当列表变大(超过 1000 个元素)时需要时间,因此无法执行全部。

我也尝试使用这组操作设置合并排序算法,但是对于 10k 的数字,它需要太多时间(超过 10 秒)。

任何人都知道快速执行这种排序的有效算法吗?我也在使用链表并在 C 中编写程序以优化效率。

【问题讨论】:

标签: c algorithm list sorting


【解决方案1】:

问题陈述中缺少将 A 的第一个元素与 B 的第一个元素进行比较的能力。

使用链表将允许快速旋转。使用归并排序应该相当快。

使用先到后轮换将 A 和 B 视为队列。将每个队列视为两个队列,一个输入(前)队列和一个输出(后)队列。使用计数来跟踪每个队列的输入和输出部分之间的边界。合并逻辑的每个输入队列都需要剩余运行计数。

对于初始传递,将元素分成两个队列,从 A 获取一个元素,追加到 B,从 A 获取另一个元素,追加到 A。完成后,A 和 B 包含大小为 1 的运行。

从 A 和 B 合并运行,再次在 A 和 B 之间交替合并运行输出,因此当合并过程完成时,队列已准备好进行下一个过程。

最终所有元素都在一个列表中,排序完成。

更新 - 我使用 C++ std::queue 为队列编写了一个测试程序,我用不到 0.4 秒的时间对我的 100 万个元素(32 位模式,32 位整数)进行排序系统(英特尔 3770k 3.5 ghz)。使用链表的 C 程序将比 std::queue 占用更少的开销,因此速度更快。

用于概念证明的示例 C++ 程序,它使用两个 std::queues,使用 front / pop / push 来有效地进行旋转。输入结构(qs0、qs1)不需要指针,但它使输入和输出引用保持一致。指向结构 (pqso) 的输出指针在两个队列结构 (qs0, qs1) 之间交替。有两个 goto 用于分支到内部循环的公共“清理”代码,它复制“其他”运行的其余部分并跳出内部循环。这个例子是基于一个旧的归并排序程序,因为这是一个“概念证明”,我没有费心去改变它。

#include <iostream>
#include <queue>
#include <cstdlib>
#include <ctime>

typedef unsigned int uint32_t;

#define min(a, b)  (((a) < (b)) ? (a) : (b))

typedef struct{
    std::queue <uint32_t> *pq;              // ptr to queue
    size_t fcnt;                            // front count
    size_t bcnt;                            // back count
    size_t rcnt;                            // run count
}QS;

void msort2q(std::queue <uint32_t> &q0, std::queue <uint32_t> &q1, size_t n)
{
QS qs0;                                     // queue 0
QS qs1;                                     // queue 1
QS *pqsi0 = &qs0;                           // input 0
QS *pqsi1 = &qs1;                           // input 1
QS *pqso;                                   // output
size_t s;                                   // run size

    if(n < 2)
        return;
    pqsi0->pq = &q0;
    pqsi0->fcnt = n;
    pqsi0->bcnt = 0;
    pqsi1->pq = &q1;
    pqsi1->fcnt = 0;
    pqsi1->bcnt = 0;
    pqso = pqsi1;
    while(1){                               // split q0
        pqso->pq->push(pqsi0->pq->front());
        pqsi0->pq->pop();
        pqso->bcnt++;
        pqsi0->fcnt--;
        if(pqsi0->fcnt == 0)
            break;
        pqso = (pqso == pqsi0) ? pqsi1 : pqsi0;
    }
    pqsi0->fcnt = pqsi0->bcnt;
    pqsi0->bcnt = 0;
    pqsi1->fcnt = pqsi1->bcnt;
    pqsi1->bcnt = 0;
    s = 1;
    while(s < n){                       // merge sort
        while(1){                       // merge a pair of runs
            pqsi0->rcnt = min(s, pqsi0->fcnt);
            pqsi0->fcnt -= pqsi0->rcnt;
            pqsi1->rcnt = min(s, pqsi1->fcnt);
            pqsi1->fcnt -= pqsi1->rcnt;
            if(pqsi0->rcnt == 0)        // if end run 0
                goto copy1;             //   copy rest of 1 and break
            if(pqsi1->rcnt == 0)        // if end run 1
                goto copy0;             //   copy rest of 0 and break
            while(1){                   // merge an element
                if(pqsi0->pq->front() <= pqsi1->pq->front()){   // if q0 <= q1
                    pqso->pq->push(pqsi0->pq->front());         //   move q0
                    pqso->bcnt++;
                    pqsi0->pq->pop();
                    pqsi0->rcnt--;
                    if(pqsi0->rcnt != 0)    // if not end run 0
                        continue;           //   continue back to while
copy1:              do{                     //  else copy rest of run 1 and break
                        pqso->pq->push(pqsi1->pq->front());
                        pqso->bcnt++;
                        pqsi1->pq->pop();
                        pqsi1->rcnt--;
                    }while (pqsi1->rcnt);
                    break;
                } else {                                        // else q1 < q0
                    pqso->pq->push(pqsi1->pq->front());         //   move q1
                    pqso->bcnt++;
                    pqsi1->pq->pop();
                    pqsi1->rcnt--;
                    if(pqsi1->rcnt != 0)    // if not end run 1
                        continue;           //   continue back to while
copy0:              do{                     //  else copy rest of run 0 and break
                        pqso->pq->push(pqsi0->pq->front());
                        pqso->bcnt++;
                        pqsi0->pq->pop();
                        pqsi0->rcnt--;
                    }while (pqsi0->rcnt);
                    break;
                }
            }
            pqso = (pqso == pqsi0) ? pqsi1 : pqsi0;     // setup for next pair
            if(pqsi0->fcnt == 0 && pqsi1->fcnt == 0)    // if end pass break
                break;
        }
        pqsi0->fcnt = pqsi0->bcnt;      // setup for next pass
        pqsi0->bcnt = 0;
        pqsi1->fcnt = pqsi1->bcnt;
        pqsi1->bcnt = 0;
        s *= 2;
    }
    if(pqsi0->fcnt == 0)                // swap if needed
        std::swap(q0, q1);
}

#define NUMELEM (1024*1024)
int main()
{
clock_t dwTimeStart;                    // clock values
clock_t dwTimeStop;
uint32_t r;
size_t i;

    std::queue <uint32_t> q0;
    std::queue <uint32_t> q1;

//      generate data

    for(i = 0; i < NUMELEM; i++ )
    {
        r  = (((uint32_t)((rand()>>4) & 0xff))<< 0);
        r += (((uint32_t)((rand()>>4) & 0xff))<< 8);
        r += (((uint32_t)((rand()>>4) & 0xff))<<16);
        r += (((uint32_t)((rand()>>4) & 0xff))<<24);
        q0.push(r) ;
    }

//      sort data
    dwTimeStart = clock();
    msort2q(q0, q1, NUMELEM);
    dwTimeStop = clock();
    std::cout << "Number of ticks " << (dwTimeStop-dwTimeStart) << std::endl;

//      check data
    while(1){
        r = q0.front();
        q0.pop();
        if(q0.size() == 0)
            break;
        if(r > q0.front()){
            std::cout << "error" << std::endl;
            break;
        }
    }
    return 0;
}

【讨论】:

  • 如果有人好奇,在 python 上使用愚蠢的快速排序实现,在 10,000 个整数上大约需要 30 秒。
  • @gowrath:这非常慢,在我微不足道的 Macbook 上对 10000 个整数进行排序大约需要 1 毫秒mergesortqsort 仅高于 100 微秒 在 C 中使用 radixsort
  • @chqrlie 读到:“一个愚蠢的实现”。如果您想分享 qsort 实现,请回答,我很乐意看到它。
  • @rcgldr 不用担心。我应该更清楚。你能发布你的合并排序解决方案吗?我正计划实施它,但还没有时间。
  • @gowrath - 自从提出问题以来已经足够长了,所以我在答案中添加了示例代码。
猜你喜欢
  • 2015-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-15
  • 2022-01-16
  • 1970-01-01
  • 2023-04-09
相关资源
最近更新 更多