遗忘比较、排序网络、非常见排序

目录

一,遗忘比较交换算法

二,0-1排序引理

三,列排序

四,比较网络

1,比较算子

2,比较网络

3,比较网络的可并行性

五,排序网络

1,排序网络

2,排序网络的判定

3,冒泡排序的排序网络

4,希尔排序的排序网络

六,奇偶排序

1,奇偶排序网络

2,实现

七,双调排序

八,奇偶移项排序


一,遗忘比较交换算法

出自算法导论:

遗忘比较交换算法是指算法只按照事先定义好的操作执行,即需要比较的位置下标必须事先确定好。

虽然算法可能依靠待排序元素的个数,但它不能依靠待排序元素的值,也不能依赖任何之前的比较交换操作的结果。

排序算法一文中有提到,插入排序、选择排序、冒泡排序、归并排序、堆排序、快速排序、希尔排序都是基于比较的排序,而这里面,冒泡排序是遗忘比较,希尔排序是嵌套了其他排序算法的,如果嵌套了遗忘比较算法,那么整个希尔排序也是遗忘比较算法,另外几个都不是遗忘比较算法。

二,0-1排序引理

0-1排序引理:如果一个遗忘比较交换算法能够对所有只包含0和1的输入序列排序,那么它也可以对包含任意值的输入序列排序。

三,列排序

即使不知道奇数步中,对每一列的排序采取的是什么排序算法,我们仍然可以认为,列排序算法是遗忘比较交换算法。

书中后面有提示,如何根据0-1排序引理证明上述八步操作可以完成排序,不难,略。

以s=5为例,我们把数组的长度补到50的倍数x(x>=250),那么r=x/5,r>=50,即满足条件,排序完成之后再把多余的元素删掉即可。

代码:

template<typename T>
void Sort(T* arr, int len, bool(*cmp)(T a, T b))
{   
    if (len < 250 || len % 50) {
        int b = max(len / 50, 5);
        int len2 = b * 50;
        T m = GetMax(arr, len);
        T* p = new T[len2];
        for (int i = 0; i < len; i++)p[i] = arr[i];
        for (int i = len; i < len2; i++)p[i] = m;
        Sort(p, len2, cmp);
        for (int i = 0; i < len; i++)arr[i] = p[i];
        return;
    }
    const int s = 5;
    int r = len / s;  //r行s列
    T* p = new T[len];
    for (int i = 0; i < len; i += r)Sort2(arr + i, r, cmp);//每一列排序
    for (int i = 0; i < len; i++) p[i / s + i % s * r] = arr[i];//转置
    for (int i = 0; i < len; i += r)Sort2(p + i, r, cmp);//每一列排序
    for (int i = 0; i < len; i++) arr[i] = p[i / s + i % s * r];//逆转置
    for (int i = 0; i < len; i += r)Sort2(arr + i, r, cmp);//每一列排序
    for (int i = r / 2; i < len - r; i += r)Sort2(arr + i, r, cmp);//最后一次排序
}

其中的sort2是调用别的排序算法,比如插入排序。

相关代码和接口定义参见 排序算法,下同。

四,比较网络

来自 OI之外的一些东西:简单谈谈排序网络 | Matrix67: The Aha Moments

1,比较算子

对于一个数列{ai},任取2个数ai和aj,比较大小并(可能)交换位置,使得ai<=aj成立,这样的一个操作叫比较算子。

PS:可以理解为这是递增比较算子,对应的还有一个递减比较算子。

2,比较网络

一系列有序的比较算子,构成一个比较网络。

例如下面是4个比较算子构成的网络:

一般的比较网络,所有的算子都是一个方向的,即要么都是递增算子要么都是递减算子,所以用图表示比较网络可以省略方向。

而对于既有递增算子又有递减算子的网络,需要用带箭头的线表示。

3,比较网络的可并行性

以上面的网络为例,中间的2个比较算子,如果改变先后顺序,或者说同时操作,显然整个网络的结果是一样的。

更一般地,如果把一个比较网络的若干比较算子的顺序交换,使得对于任意下标i,涉及到第i个元素的所有比较算子之间的前后顺序不变,那么整个网络就和原网络等价

PS:这是我的推理,没有严格的论证。

五,排序网络

1,排序网络

如果一个比较网络对于任何输入数列,输出的都是有序数列,那么称这个比较网络为排序网络。

上面的比较网络不是排序网络,如输入3 1 4 2输出的是1 3 2 4,不是有序的。

2,排序网络的判定

排序网络和遗忘比较交换算法是对应的。

所以判定一个比较网络是不是排序网络,可以用0-1排序引理。

即如果一个比较网络能够对所有只包含0和1的输入序列排序,那么它就是排序网络。

3,冒泡排序的排序网络

 可以调整为:

 这样,就可以提高并行程度,加快速度。

4,希尔排序的排序网络

希尔排序

六,奇偶排序

1,奇偶排序网络

不带浅色的是7个元素的排序网络,带浅色的是8个元素的排序网络。

排序网络一目了然,算法步骤就不用描述了。

matrix67的博文中用0-1排序引理和数学归纳法证明了此算法的正确性。

2,实现

template<typename T>
void Sort(T* arr, int len, bool(*cmp)(T a, T b))
{
    for (int i = 0; i < len; i++) {
        for (int j = i % 2; j + 1 < len; j += 2) {
            if (cmp(arr[j + 1], arr[j]))exchange(arr + j, arr + j + 1);
        }
    }
}

七,双调排序

双调就是单峰的意思。

1,双调排序网络

wiki-16-sequnce-sort

算法可以分为三块:

 左上就是一个排序,和整个图是一样的。

左下和左上是对称的,也就是说,经过左边2个步骤之后,整个数列是单峰的。

右边的是把一个单峰数列变成一个有序数列。

2,实现

对于长度不是2的幂的数列,需要补齐长度。

template<typename T>
bool cmp2(T a, T b, int dir)
{
	if (dir == 0)return cmp(a, b);
	else return cmp(b, a);
}

template<typename T>
void Sort(T* arr, int len, bool(*cmp)(T a, T b), int dir) // dir: 0 默认cmp 1 相反顺序
{
	if (len <= 1)return;
	Sort(arr, len / 2, cmp, dir);
	Sort(arr + len / 2, len / 2, cmp, 1 - dir);
	for (int k = 1; k < len; k *= 2) {
		int d = len / k;
		for (int s = 0; s < len; s += d) {
			for (int i = s; i < s + d / 2; i++)
				if (cmp2(arr[i + d / 2], arr[i], dir))exchange(arr + i + d / 2, arr + i);
		}
	}
}

template<typename T>
void Sort(T* arr, int len, bool(*cmp)(T a, T b))
{
	int maxLen = 1;
	while (maxLen < len)maxLen *= 2;
	T* arr2 = new T[maxLen];
	T m = GetMax(arr, len);
	for (int i = 0; i < len; i++)arr2[i] = arr[i];
	for (int i = len; i < maxLen; i++)arr2[i] = m;
	Sort(arr2, maxLen, cmp, 0);
	for (int i = 0; i < len; i++)arr[i] = arr2[i];
	delete[]arr2;
}

3,等价形式

Alternative representation

相当于把上面的网络的左下部分翻转一下,经过递归就得到方向一致的网络。

实现:

template<typename T>
void Sort2(T* arr, int len, bool(*cmp)(T a, T b)) 
{
	if (len <= 1)return;
	Sort2(arr, len / 2, cmp);
	Sort2(arr + len / 2, len / 2, cmp);
	for (int i = 0, j = len - 1; i < j; i++, j--) {
		if (cmp(arr[j], arr[i]))exchange(arr + j, arr + i);
	}
	for (int k = 2; k < len; k *= 2) {
		int d = len / k;
		for (int s = 0; s < len; s += d) {
			for (int i = s; i < s + d / 2; i++)
				if (cmp(arr[i + d / 2], arr[i]))exchange(arr + i + d / 2, arr + i);
		}
	}
}

template<typename T>
void Sort(T* arr, int len, bool(*cmp)(T a, T b))
{
	int maxLen = 1;
	while (maxLen < len)maxLen *= 2;
	T* arr2 = new T[maxLen];
	T m = GetMax(arr, len);
	for (int i = 0; i < len; i++)arr2[i] = arr[i];
	for (int i = len; i < maxLen; i++)arr2[i] = m;
	Sort2(arr2, maxLen, cmp);
	for (int i = 0; i < len; i++)arr[i] = arr2[i];
	delete[]arr2;
}

八,奇偶移项排序

1,算法思路

奇偶移项排序和归并排序类似,差别在于合并过程是遗忘算法,所以整个排序是遗忘算法。

奇偶移项排序和双调排序一样,首先补齐元素,使得元素个数是2的幂,然后前面一半和后面一半递归排序,最后进行合并。

合并过程也是一个递归的过程,首先合并所有的奇数项,然后合并所有的偶数项,最后把所有的偶数项(除了最后一个)和它下一项比较交换,这样就完成了合并。

整个排序的时间复杂度是 O(n*logn*logn)

2,排序网络

以n=8为例:

3,实现

template<typename T>
void Merge(T* arr, int len, int d, bool(*cmp)(T a, T b))
{
    if (d >= len / 2) {
        if (cmp(arr[d], arr[0]))exchange(arr + d, arr);
        return;
    }
    Merge(arr, len, d * 2, cmp);
    Merge(arr + d, len - d, d * 2, cmp);
    for (int i = d; i < len - d; i += d * 2) {
        if (cmp(arr[i + d], arr[i]))exchange(arr + i + d, arr + i);
    }
}

template<typename T>
void Sort2(T* arr, int len, bool(*cmp)(T a, T b)) 
{
	if (len <= 1)return;
	Sort2(arr, len / 2, cmp);
	Sort2(arr + len / 2, len / 2, cmp);
    Merge(arr, len, 1, cmp);
}

 

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,我们需要安装 `scikit-learn` 和 `pyspc` 库。可以使用以下命令进行安装: ``` pip install -U scikit-learn pyspc ``` 然后,我们可以使用以下代码实现使用 Minkowski 距离减轻增量学习中的灾难性遗忘问题: ```python from sklearn.neighbors import KNeighborsClassifier from pyspc import spc class IncrementalClassifier: def __init__(self, k, p): self.k = k self.p = p self.model = KNeighborsClassifier(n_neighbors=k, p=p) self.data = None self.targets = None def fit(self, X, y): if self.data is None: self.data = X self.targets = y else: # 更新数据集 self.data = np.vstack([self.data, X]) self.targets = np.hstack([self.targets, y]) # 训练模型 self.model.fit(self.data, self.targets) def predict(self, X): return self.model.predict(X) def forget(self, n): if self.data is not None: # 检查要删除的数据数量是否超过数据集大小 n = min(n, len(self.data)) # 计算数据集中每个样本的 SPC 值 spc_values = spc(self.data) # 根据 SPC 值对数据集进行排序 sorted_indices = np.argsort(spc_values) # 删除 SPC 值最小的 n 个样本 self.data = np.delete(self.data, sorted_indices[:n], axis=0) self.targets = np.delete(self.targets, sorted_indices[:n]) # 重新训练模型 self.model.fit(self.data, self.targets) ``` 在这个代码中,我们使用 `KNeighborsClassifier` 作为分类器模型,并且实现了 `fit` 和 `predict` 方法来训练和预测模型。我们还实现了 `forget` 方法来删除数据集中的一些数据,以避免灾难性遗忘问题。 删除数据的过程涉及到计算数据集中每个样本的 SPC 值,并根据 SPC 值对数据集进行排序。我们使用了 `pyspc` 库来计算 SPC 值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值