分段双调排序

分段双调排序 - Shuai-Xie -Github

问题说明

给出分成 m 段的 n 个浮点数,输入数据已按段号有序,但每段内部无序。
用 C/C++ 编写一个分段双调排序(Bitonic sort)函数,对每一段内部的浮点数进行排序,但不要改变段间的位置。

1. 接口方式

// 分段双边排序
void segmentedBitonicSort(float* data, int* seg_id, int* seg_start, int n, int m); 
  • data 包含需要分段排序的 n 个 float 值(由下面的 seg_id 可知是表示多段的数据)
  • seg_id 给出 data 中 n 个元素各自所在的段编号
  • seg_start 共有 m+1 个元素,前 m 个分别给出 0..m-1 共 m 个段的起始位置,seg_start[m] = n
  • n 表示 data 中包含 n 个数据
  • m 表示 data 分为 m 段数据

由题意得,m <= n,因为 data 某一段可能有多于 1 个数据,这种情况下:m < n

seg_id 中的元素保证单调不下降,即对任意的 i < j,seg_id[i] <= seg_id[j]
seg_id 所有元素均在 0 到 m-1 范围内。(因为是段 id,m 段就是0..m-1)

2. 输入输出

输入

float data[5] = {0.8, 0.2, 0.4, 0.6, 0.5};
int seg_id[5] = {0, 0, 1, 1, 1}; // 0,1 所在位置元素对应的段id
int seg_start[3] = {0, 2, 5}; // 表示第1段从0开始:{0.8, 0.2},第2段从2开始:{0.4, 0.6, 0.5}
int n = 5; // 总共5个数
int m = 2; // 2段

输出

float data[5] = {0.2, 0.8, 0.4, 0.5, 0.6};

3. 其他要求

3.1 注意
  1. 必须使用双调排序算法进行排序。
  2. 可以直接使用从网上下载的双调排序代码,但须注明出处。
3.2 加分挑战(非必需)
  1. 不递归:segmentedBitonicSort 函数及其所调用的任何其他函数都不得直接或间接地进行递归。
  2. 不调用函数:segmentedBitonicSort 不调用除标准库函数外的任何其他函数。
  3. 内存高效:segmentedBitonicSort 及其所调用的任何其他函数都不得进行动态内存分配,包括malloc、new和静态定义的STL容器。
  4. 可并行:segmentedBitonicSort 涉及到的所有时间复杂度O(n)以上的代码都写在for循环中,而且每个这样的for循环内部的循环顺序可以任意改变,不影响程序结果。注:自己测试时可以用rand()决定循环顺序。
  5. 不需内存:segmentedBitonicSort 不调用任何函数(包括C/C++标准库函数), 不使用全局变量,所有局部变量都是int、float或指针类型,C++程序不使用new关键字。
  6. 绝对鲁棒:在输入数据中包含 NaN 时(例如sqrt(-1.0)),保证除NaN以外的数据正确排序,NaN的个数保持不变。

NaN,是 Not a Number 的缩写。
NaN 用于处理计算中出现的错误情况,比如 0.0 除以 0.0 或者求负数的平方根。

你的程序每满足以上的一个条件都可以获得额外的加分。

3.3 应提交的结果
  1. 算法描述;
  2. 尝试过和完成了的加分挑战;
  3. 可以独立运行的源代码;
  4. 测试数据;
  5. 性能分析;
  6. 测试的起始和完成时间以及实际使用的时间。
3.4 提示
  1. 利用好网上资源。
  2. 尽量利用输入中的冗余信息。
  3. 利用好位操作。 (>>1)

解答

1. 递归分段双调排序

#include <iostream>
#include <iomanip>

using namespace std;

/**
 * @param arr 小数数列
 * @param len 数列长度
 * @param w 输出单个小数的长度
 * @param precision 小数的精度
 */
void printFloatArr(float *arr, int len) {
    for (int i = 0; i < len; ++i) {
        cout << setw(4) << arr[i];
    }
    cout << endl;
}

int getGreatest2nLessThan(int len) {
    int k = 1;
    while (k < len) k = k << 1; // 注意一定要加k=
    return k >> 1;
}

// 任意双调排序归并
void bitonicMergeAnyN(float *arr, int len, bool asd = true) {
    if (len > 1) {
        int m = getGreatest2nLessThan(len);
        for (int i = 0; i < len - m; ++i) {
            if (arr[i] > arr[i + m] == asd)
                swap(arr[i], arr[i + m]); // 根据asd判断是否交换
        }
        bitonicMergeAnyN(arr, m, asd); // 一般情况下,m > len-m
        bitonicMergeAnyN(arr + m, len - m, asd);
    }
}

// 双调排序
void bitonicSort(float *arr, int len, bool asd) { // asd 升序
    if (len > 1) {
        int m = len / 2;
        bitonicSort(arr, m, !asd); // 前半降序
        bitonicSort(arr + m, len - m, asd); // 后半升序
        bitonicMergeAnyN(arr, len, asd);
    }
}

/**
 * 分段双调排序
 * @param data 原始数据
 * @param seg_id data 中 n 个元素各自所在的段编号
 * @param seg_start 共有 m+1 个元素,前 m 个分别给出 0..m-1 共 m 个段的起始位置,seg_start[m] = n
 * @param n data 中包含 n 个数据
 * @param m data 分为 m 段数据
 */
void segmentedBitonicSort(float *data, int *seg_id, int *seg_start, int n, int m) {
    bool asd = true;
    for (int i = 0; i < m; ++i) {
        float *arr = data + seg_start[i]; // 数列起点
        int len = seg_start[i + 1] - seg_start[i]; // 数列长度
        bitonicSort(arr, len, asd);
        cout << "段 " << i << ": ";
        printFloatArr(arr, len);
    }
}


int main() {
    float data[7] = {0.8, 0.2, 0.4, 0.6, 0.5, 0.2, 0.1}; // 数列
    int seg_id[7] = {0, 0, 1, 1, 1, 2, 2}; // id
    int seg_start[4] = {0, 2, 5, 7}; // 段首位置
    int n = 7; // 数据长度
    int m = 3; // 数据段数

    segmentedBitonicSort(data, seg_id, seg_start, n, m);
}
段 0:  0.2 0.8
段 1:  0.4 0.5 0.6
段 2:  0.1 0.2

2. 解决NaN问题

在归并的时候,遇到NaN型数据,就直接跳到下一个数据。

// 任意双调排序归并
void bitonicMergeAnyN(float *arr, int len, bool asd = true) {
    if (len > 1) {
        int m = getGreatest2nLessThan(len);
        for (int i = 0; i < len - m; ++i) {

            // 解决NaN问题
            int low = i;
            while (arr[low] == NaN) low++;
            int high = i + m;
            while (arr[high] == NaN) high++;
            // 核心代码

            if (arr[low] > arr[high] == asd)
                swap(arr[low], arr[high]); // 根据asd判断是否交换
        }
        bitonicMergeAnyN(arr, m, asd); // 一般情况下,m > len-m
        bitonicMergeAnyN(arr + m, len - m, asd);
    }
}

完整代码

#include <iostream>
#include <iomanip>
#include <cmath>

#define NaN (float)sqrt(-1.0)

using namespace std;

/**
 * @param arr 小数数列
 * @param len 数列长度
 * @param w 输出单个小数的长度
 * @param precision 小数的精度
 */
void printFloatArr(float *arr, int len) {
    for (int i = 0; i < len; ++i) {
        if (arr[i] == NaN) cout << setw(5) << "NaN";
        else cout << setw(5) << arr[i];
    }
    cout << endl;
}

int getGreatest2nLessThan(int len) {
    int k = 1;
    while (k < len) k = k << 1; // 注意一定要加k=
    return k >> 1;
}

// 任意双调排序归并
void bitonicMergeAnyN(float *arr, int len, bool asd = true) {
    if (len > 1) {
        int m = getGreatest2nLessThan(len);
        for (int i = 0; i < len - m; ++i) {

            // 解决NaN问题
            int low = i;
            while (arr[low] == NaN) low++;
            int high = i + m;
            while (arr[high] == NaN) high++;

            if (arr[low] > arr[high] == asd)
                swap(arr[low], arr[high]); // 根据asd判断是否交换
        }
        bitonicMergeAnyN(arr, m, asd); // 一般情况下,m > len-m
        bitonicMergeAnyN(arr + m, len - m, asd);
    }
}

// 双调排序
void bitonicSort(float *arr, int len, bool asd) { // asd 升序
    if (len > 1) {
        int m = len / 2;
        bitonicSort(arr, m, !asd); // 前半降序
        bitonicSort(arr + m, len - m, asd); // 后半升序
        bitonicMergeAnyN(arr, len, asd);
    }
}

/**
 * 分段双调排序
 * @param data 原始数据
 * @param seg_id data 中 n 个元素各自所在的段编号
 * @param seg_start 共有 m+1 个元素,前 m 个分别给出 0..m-1 共 m 个段的起始位置,seg_start[m] = n
 * @param n data 中包含 n 个数据
 * @param m data 分为 m 段数据
 */
void segmentedBitonicSort(float *data, int *seg_id, int *seg_start, int n, int m) {
    bool asd = true;
    for (int i = 0; i < m; ++i) {
        float *arr = data + seg_start[i];
        int len = seg_start[i + 1] - seg_start[i];
        bitonicSort(arr, len, asd);
        cout << "段 " << i << ": ";
        printFloatArr(arr, len);
    }
}


int main() {

    float data[11] = {0.8, 0.2, 0.4, NaN, 0.6, 0.5, NaN, 0.2, 0.1, NaN, 0.01}; // 数列
    int seg_id[11] = {0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2}; // id
    int seg_start[4] = {0, 2, 6, 11}; // 段首位置
    int n = 11; // 数据长度
    int m = 3; // 数据段数

    segmentedBitonicSort(data, seg_id, seg_start, n, m);
}
段 0:   0.2  0.8
段 1:   0.4  NaN  0.5  0.6
段 2:   NaN 0.01  0.1  NaN  0.2
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容

  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,134评论 30 470
  • 概述 排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部...
    蚁前阅读 5,170评论 0 52
  • 1.插入排序—直接插入排序(Straight Insertion Sort) 基本思想: 将一个记录插入到已排序好...
    依依玖玥阅读 1,245评论 0 2
  • 概率需求模型:单期订货 美式足球在美国非常受欢迎,不管是职业联赛、大学、中学、甚至小学都热衷于这个运动,非常有代表...
    供应链NSGG阅读 1,030评论 0 1
  • 几件事情: (1)、寄快递,给罗总发短信,给肖老师发短信,给姚哥发短信,给辉哥发短信,给崔杨发短信,给朱总发短信,...
    笑曰阅读 208评论 0 0