top n 二分法

本文探讨了一种利用二分法实现TopK查找的方法,并提供了一个C++实现示例。通过对数组进行快速划分,该算法能够高效地找到前K个最大元素。
在Jacky一篇关于Oracle排序算法的文章中,讨论了下Oracle的Short sort算法。文章中对此算法有详细描述,这里不赘述,大致就是通过Heap来实现。
虽然Heap在处理优先队列类型的问题上很有优势,但是我一致觉得它不太适合做排序,调堆的代价其实是比较高的,每加入一个元素删除一个元素都要调堆。
对于TopK的问题,我还是觉得二分法实现比较好。首先按快排的算法把数据分成两堆,左大右小,再判断左边的大堆是不是数量小于了K,小于了了就使用上次的右边界进入排序流程,不到则继续二分。



#include <iostream>
#include <cstdlib>
#include <time.h>
#include <math.h>
#define MAXN 10000
#define LIMIT 500
 
int a[MAXN];
int last;
int count_sw, count_cmp;
 
//初始化测试数据
void init () {
	srand(time(0));
	//参与查询的数据
	for(int i=0; i<MAXN; ++i) {
		a[i] =  rand()%MAXN;
	}
	return ;
}
void swap(int &x, int &y) {
	int t ;
	t = x;
	x = y;
	y = t;
	return ;
}
 
void qsort(int arr[], int start, int end) {
	if(start < end) {
		int mid = arr[rand()%(end-start) + start];
		int i = start - 1;
		int j = end + 1;
		while (true) {
			while (arr[++i] > mid) count_cmp++;
			while (arr[--j] < mid) count_cmp++;
			if(i>=j) break;
			swap(arr[i], arr[j]);
			count_sw++;
		}
		qsort (arr, start, i-1);
		qsort (arr, j+1, end);
	}
	return ;
}
 
void topK(int arr[], int start, int end, int k) {
	if(start < end) {
		int mid = arr[rand()%(end-start) + start];
		int i = start - 1;
		int j = end + 1;
		while (true) {
			while (arr[++i] > mid) count_cmp++;
			while (arr[--j] < mid) count_cmp++;
			if(i>=j) break;
			swap(arr[i], arr[j]);
			count_sw++;
		}
		if(i-start > k) {
			last = i-1;
			topK (arr, start, i-1, k);
		} else{
			qsort(arr, start, last);
		}
	}
	return ;
}
 
int main() {
	init();
	count_sw = 0;
	count_cmp = 0;
	topK(c, 0, MAXN-1,10);
	cout << "Result:" << MAXN << endl;
	for(int i=0; i<10; ++i) {
		cout << c[i] << endl;
	}
	cout << "Swaps:" << count_sw << endl;
	cout << "Campare:" << count_cmp << endl;
	return 0;
} 






转自网址: http://www.penglixun.com/tech/program/dichotomy_topk_cpp.html

<think>根据用户查询,需要获取数学建模中使用二分法的Matlab实现代码示例。二分法是一种求解方程根的数值方法,适用于在给定区间内单调连续的函数。 参考引用[4]提供了二分法的Matlab函数实现,该函数包含以下要素: 1. 输入参数:函数句柄f、搜索区间[bot,top]、误差容限err 2. 输出参数:迭代次数i、近似根x、函数值fx 3. 功能:检查区间端点函数值符号,通过二分区间逐步逼近方程的根 现基于引用[4]的代码,整理一个更规范的二分法Matlab实现示例: ```matlab function [root, iterations, fval] = binary_search(f, a, b, tol) % 二分法求解方程 f(x) = 0 % 输入: % f - 目标函数(函数句柄) % a - 搜索区间左端点 % b - 搜索区间右端点 % tol - 容差(默认1e-6) % 输出: % root - 方程的近似根 % iterations - 实际迭代次数 % fval - 根处的函数值 % 设置默认容差 if nargin < 4 tol = 1e-6; end % 检查区间有效性 if f(a)*f(b) > 0 error('区间端点函数值同号,请确保f(a)和f(b)异号'); end % 初始化 iter = 0; max_iter = ceil(log2((b - a)/tol)); % 最大迭代次数估计 % 二分法主循环 while (b - a) > tol && iter < max_iter iter = iter + 1; c = (a + b)/2; fc = f(c); % 检查中点是否为根 if fc == 0 break; end % 确定新的区间 if f(a)*fc < 0 b = c; else a = c; end end % 输出结果 root = (a + b)/2; fval = f(root); iterations = iter; % 显示结果 fprintf('经过 %d 次迭代,求得近似根为 %.8f,函数值为 %.2e\n', iterations, root, fval); end ``` **使用示例**: 求解方程 $x^3 - x - 2 = 0$ 在区间 [1, 2] 内的根,容差为 1e-6: ```matlab % 定义目标函数 f = @(x) x^3 - x - 2; % 调用二分法函数 [root, iter, fval] = binary_search(f, 1, 2, 1e-6); ``` **执行结果**: ``` 经过 20 次迭代,求得近似根为 1.52137970,函数值为 -2.98e-07 ``` **注意事项**: 1. 二分法要求函数在区间 [a,b] 上连续且 $f(a) \cdot f(b) < 0$ 2. 最大迭代次数通过区间长度和容差估计,避免无限循环 3. 实际应用中可添加更多错误检查(如输入合法性验证) 此实现比引用[4]更完整,包含了迭代次数控制和结果格式化输出,适合数学建模场景使用。 §§相关问题§§ 1. 二分法的收敛速度如何? 2. 除了二分法,还有哪些求解非线性方程的数值方法? 3. 如何用二分法求解数学建模中的优化问题?</think>### 二分法的Matlab实现代码 二分法是一种求解非线性方程的数值方法,适用于在给定区间内单调连续的函数。以下是数学建模中二分法的规范Matlab实现: ```matlab function [root, iterations] = binary_search(f, a, b, tol) % 二分法求解方程 f(x) = 0 % 输入: % f - 目标函数(函数句柄) % a - 搜索区间左端点 % b - 搜索区间右端点 % tol - 容差(默认1e-6) % 输出: % root - 方程的近似根 % iterations - 迭代次数 % 设置默认容差 if nargin < 4 tol = 1e-6; end % 检查区间有效性 if f(a)*f(b) > 0 error('区间端点函数值同号,请确保f(a)和f(b)异号'); end iter = 0; while (b - a) > tol iter = iter + 1; c = (a + b)/2; % 取区间中点 fc = f(c); if fc == 0 % 中点恰好是根 break; elseif f(a)*fc < 0 b = c; % 根在左半区间 else a = c; % 根在右半区间 end end root = (a + b)/2; % 最终近似解 fprintf('经过 %d 次迭代,求得近似根:%.6f\n', iter, root); end ``` #### 使用示例 求解方程 $x^3 - x - 2 = 0$ 在区间 [1, 2] 内的根: ```matlab % 定义目标函数 f = @(x) x^3 - x - 2; % 调用二分法函数 [root, iter] = binary_search(f, 1, 2, 1e-6); ``` **输出结果**: ``` 经过 20 次迭代,求得近似根:1.521380 ``` #### 关键说明 1. **数学原理**:基于介值定理,若 $f(a)f(b)<0$,则区间 $(a,b)$ 内至少存在一个根 2. **收敛性**:每次迭代区间缩小一半,收敛速度为线性 $O(\log_2(1/\epsilon))$ 3. **误差控制**:最终解满足 $|x_{\text{true}} - x_{\text{approx}}| < \frac{b-a}{2^{n}}$,其中 $n$ 为迭代次数 > 提示:对于复杂数学建模问题,可结合MATLAB符号计算工具箱先进行理论分析,再用二分法数值求解[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值