编程之美2.5——寻找最大的K个数

本文介绍在不同数据规模下选择最大K个数的有效算法。对于小规模数据,采用顺序统计法;对于中等规模数据,使用二分中值法;而对于大规模数据,则推荐使用最小堆法。这些方法适用于数百至数万亿规模的数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题:

从一组数中选出其中最大的K个数,当这组数的个数为几百、几百万、几百亿时分别适合采用哪些算法?

 

个数为几百时,使用顺序统计法(看算法导论第9章):

     算法思想是对输入数组进行递归划分,一边的数据小于选定数,另一边的数据大于等于选定数。但和快速排序不同的是,快速排序会递归处理划分的两边,而顺序统计法只处理划分的一边。其随机化算法的期望时间为O(n)。除了无法处理大规模的数据外,它还有一个缺点,就是会改变输入数据的顺序,也就是说算法不是稳定的。

#include <iostream>
#include <cstdlib>
using namespace std;

#define MAXN 103
int A[MAXN];

void select(int u, int v, int k)
{
	int s = rand()%(v-u+1)+u;
	int a = A[s];
	A[s] = A[u];
	A[u] = a;
	int i, j=u;
	for (i=u; i<=v; i++)
		if (A[i] > a)
		{
			int tmp = A[++j];
			A[j] = A[i];
			A[i] = tmp;
		}
	A[u] = A[j];
	A[j] = a;
	if (j == k) return;
	else if (j < k)
		select(j+1, v, k);
	else
		select(u, j-1, k);
}

int main()
{
	int n, k, i, j;
	cin >> n >> k;
	for (i=0; i<n; i++)
		cin >> A[i];
	select(0, n-1, k-1);
	for (i=0; i<k; i++)
		cout << A[i] << " ";
	cout << endl;
}

个数为几百万时,数据量较大不适合全装入内存中,能容忍多次访问,可使用二分中值法(用法有点奇怪,个人不太喜欢):

      本质上是通过二分思想找出第K大的数的值。算法从[Min, Max]开始逐渐缩小第K大的数取值的范围,时间复杂度为O(N*log(Max-Min))。

#include <iostream>
#include <cstdlib>
using namespace std;


int binary(FILE *in, int v)
{
	rewind(in);
	int a, sum = 0;
	while (fscanf(in, "%d", &a)!=EOF)
	{
		if (a >= v) sum++;
	}
	return sum;
}

void finded(FILE *in, int v)
{
	rewind(in);
	int a;
	while (fscanf(in, "%d", &a)!=EOF)
	{
		if (a >= v) 
			cout << a << " ";
	}
	cout << endl;
}

int main()
{
	int n, k;
	cin >> n >> k;
	FILE* in = fopen("dat.txt","r");
	int min, max;
	int a;
	fscanf(in, "%d", &a);
	min = max = a;
	while (fscanf(in, "%d", &a)!=EOF)
	{
		if (a < min) min = a;
		if (a > max) max = a;
	}
	while (max > min)
	{
		int mid = (min+max)/2;
		int ns = binary(in, mid);
		if (ns == k)
		{
			finded(in, (min+max)/2);
			break;
		}
		else if (ns < k) max = mid;
		else min = mid;
	}
}

 

个数为几万亿时,数据量较大不适合全装入内存中,且无法容忍多次访问,所有数据只能访问一次,推荐使用最小堆法(上面那种情况也推荐使用这个方法),但要求K较小,否则无法在内存中存下整个最小堆。

      用容量为K的最小堆来存储最大的K个数,最小堆的堆顶元素就是最大K个数中最小的一个。每次考虑一个新的元素时,将其与堆顶的元素进行比较,只有当它大于堆顶元素时,才用其替换堆顶元素,并更新最小堆。时间复杂度为O(N*logK)。

#include <iostream>

using namespace std;

#define MAXN 103

int H[MAXN];

void upshift(int s)
{
	int tmp = H[s];
	while (s>1 && H[s>>1] > tmp)
	{
		H[s] = H[s>>1];
		s >>= 1;
	}
	H[s] = tmp;
}

void downshift(int n)
{
	int tmp = H[1];
	int i=1, j=i<<1;
	while (j <= n)
	{
		if (j+1 <= n && H[j+1] < H[j]) j++;
		if (H[j] < tmp) H[i] = H[j];
		else break;
		i = j;
		j = j<<1;
	}
	H[i] = tmp;
}

int main()
{
	int n, k, i, A;
	cin >> n >> k;
	for (i=1; i<=k; i++)
	{
		cin >> H[i];
		upshift(i);
	}
	for (; i<=n; i++)
	{
		cin >> A;
		if (A > H[1])
		{
			H[1] = A;
			downshift(k);
		}
	}
	for (i=1; i<=k; i++)
		cout << H[i] << " ";
	cout << endl;
}

 

``` {——估值优化——} DNP_NORM := IF(FINANCE(33) > 0 AND FINANCE(1) > 0, C / ((FINANCE(33) * FINANCE(34)) / FINANCE(1)), 9999); // 核心修改点:补充完整的 IF 嵌套结构,并确保所有括号闭合。 DNP := IF(FINANCE(33) > FINANCE(34) * 0.15, DNP_NORM * MOM(C, 21), IFF(DNP_NORM < 9999, DNP_NORM, 9999)); PB_RATE := IF((FINANCE(5) / FINANCE(1)) > 0.15, (C / FINANCE(5)) * SQRT(FINANCE(33) / FINANCE(1)) * INDBLOCK(5) / 100, 9999); PEG := DNP / EMA(MAX(FINANCE(54) / FINANCE(34) * 100, 0.15), 8); IND_RS := EMA(((C / REF(C, 21))) / (INDEXC / REF(INDEXC, 21)) * 2.618, 13) * CORR(C, INDEXC, 55); {——波动优化——} VOLAT := EMA(STD(C, 34) / EMA(C, 34), 55) * SQRT(252); VAR_P := IF(VOLAT < 0.12, 144, IF(VOLAT < 0.18, 89, IF(VOLAT < 0.25, 55, 34))); FST_L := LLVBARS(C, VAR_P) * 0.618 + HHVBARS(C, VAR_P) * 0.382; SLW_L := CEILING(VAR_P * 1.618 * VOLAT / 0.2); SGN_L := IF(VAR_P <= 34, 5, IF(VAR_P <= 89, 8, 13)); {——资金流——} MAIN_F := (L2_AMO(0, 2) + L2_AMO(1, 1) - L2_AMO(0, 3)) / CAPITAL * VOLAT / 0.2; FUND_T := EMA(MAIN_F, 5) * 2.236 + EMA(MAIN_F, 13) * 1.618 - EMA(MAIN_F, 34) * 0.618; MNY_TR := SUM(MAIN_F > 0.06, 5) >= 4 AND SLOPE(FUND_T, 5) > 0.05 AND (HHV(FUND_T, 13) - LLV(FUND_T, 13)) / LLV(FUND_T, 13) < 0.3; HT_IDX := EMA(COUNT(C = ZTPRICE(REF(C, 1), 0.1), 21) / 21 * VOL / CAPITAL, 5) * 3 + FINANCE(25) / FINANCE(1) * 2 + L2_AMO(0, 2) / CAPITAL * 1.5; {——周期共振——} MTH_MA := EMA(C, 610) * 0.8 + EMA(C, 144) * 0.2; MTH_TR := C > MTH_MA * 1.08 AND SLOPE(MTH_MA, 8) > 0.005 AND C > EMA(C, 233); WK_D := EMA(C, 55) - EMA(C, 233); WK_EA := EMA(WK_D, 34); WK_MCD := CROSS(WK_D, WK_EA) AND WK_D > 0 AND WK_D > EMA(WK_D, 21); DAY_B := C > HHV(H, 55) * 0.997 AND V > MA(V, 89) * 1.8 AND ABS((C - L) / L) < 0.015 AND (O - L) / (H - L) > 0.618; DAY_RS := RSI(5, 13, 34) > 75 AND RSI(13, 34, 55) > RSI(5, 13, 34) * 1.05; M60_BRK := C#MIN60 > HHV(H, 144)#MIN60 * 0.995 AND V#MIN60 > MA(V, 89)#MIN60 * 1.5 AND C#MIN60 / DYNAINFO(11) > 1.08; M60_KDJ := CROSS(K#MIN60, D#MIN60) AND J#MIN60 > 85 AND J#MIN60 > REF(J#MIN60, 8) * 1.3; {——风控PRO——} DBT_FLT := FINANCE(42) / FINANCE(1) < INDBLOCK(25) / 100; CSH_FLW := FINANCE(25) / FINANCE(1) > INDBLOCK(75) / 100; BTA_FLT := SLOPE(C / INDEXC, 144) BETWEEN(0.9, 1.1) AND CORR(C, INDEXC, 89) > 0.6; {——信号引擎——} STR_SIG := MTH_TR AND IND_RS > 2.2; CMP_SIG := WK_MCD AND FUND_T > 0.12; TCT_SIG := DAY_B AND DAY_RS AND M60_BRK AND M60_KDJ; FN_SIG := STR_SIG AND CMP_SIG AND TCT_SIG AND DBT_FLT AND CSH_FLW AND BTA_FLT AND PEG < 0.65 AND DNP < 18 AND FUND_T > 0.15 AND MNY_TR AND HT_IDX > 2.5 AND COUNT(FN_SIG, 89) = 0 AND BETWEEN(TIME, 93000, 145700) AND DYNAINFO(17) > 0.5; CLS_SIG := STR_SIG AND CMP_SIG AND C > EMA(C, 89) * 1.1 AND V > MA(V, 144) * 2.2 AND FUND_T > 0.2 AND FINVALUE(0) = 0; 选股条件: FN_SIG;```你的身份是高级编程技术专家,精通各类编程语言,能对编程过程中的各类问题进行分析和解答。我的问题是【我正在编辑【通达信条件选股】代码,遇到了 【错误句 : 详细信息 : 单词最大字符数不得超过 15 个 错误起始位置 : 32 ; 长度: 2】,请帮我检查并改正错误点补全正确代码,用通达信规范语法生成优化后完整代码。原有选股逻辑完整保留。
最新发布
03-30
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值