NC119 最小的K个数

该博客探讨了一个在给定数组中找到最小k个数的问题,要求空间复杂度为O(n)且时间复杂度为O(nlogn)。作者提出了一种改进的冒泡排序算法,首先对数组进行排序,然后选择前k个最小的元素。这种方法虽然简单,但在大数据集上可能效率较低。博客还提供了代码实现,并强调了优化算法的重要性。

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

描述

给定一个长度为 n 的可能有重复值的数组,找出其中不去重的最小的 k 个数。例如数组元素是4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4(任意顺序皆可)。
数据范围:0\le k,n \le 100000≤k,n≤10000,数组中每个数的大小0 \le val \le 10000≤val≤1000
要求:空间复杂度 O(n)O(n) ,时间复杂度 O(nlogn)O(nlogn)

在这里插入图片描述
思路:先用冒泡排序算法排序,存在一个List里面,然后输出List里面的元素

在这里插入图片描述

using System;
using System.Collections.Generic;

class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值
* @param input int整型一维数组
* @param k int整型
* @return int整型一维数组
*/
public List GetLeastNumbers_Solution (List input, int k) {
// write code here
List outResul = new List();
for(int i =0;i<input.Count-1;i++){
for(int j = 0;j<input.Count-1-i;j++){
if(input[j]>input[j+1]){
int temp =0;
temp = input[j];
input[j]=input[j+1];
input[j+1]=temp;
}
}
}
for(int i =0;i<k;i++){
outResul.Add(input[i]);
}
return outResul;

}

}

参考链接:
原文出处

<think>嗯,我现在需要优化直线拟合的代码,使用Huber损失函数,并且用非线性迭代优化方法。首先,我得理清楚Huber损失函数的概念和它在拟合中的应用。Huber损失函数是鲁棒回归的一种方法,结合了均方误差和绝对误差的优点,对异常值那么敏感。那它的数学表达式应该是分段的,当误差较小时用平方项,较大时用线性项,对吧? 接下来是直线方程的问题。给定的直线方程是Nc*x + Nr*y - Dist = 0。这里的变量K包含Nc、Nr和Dist,这三个参数需要优化。但是,通常直线参数会有归一化的要求,比如Nc² + Nr² = 1,这样可以避免参数缩放带来的问题。过用户这里没有提到归一化,可能在优化过程中需要考虑是否需要添加约束,或者通过参数化方法来处理。过用户可能希望直接优化这三个变量,需要额外的约束,所以可能需要检查是否需要在目标函数中加入正则化项,或者是否在优化过程中处理这个问题。 然后,拟合误差e_i的计算。每个点到直线的距离公式应该是e_i = |Nc*x_i + Nr*y_i - Dist| / sqrt(Nc² + Nr²)。过这里用户给出的直线方程是Nc*x + Nr*y - Dist = 0,所以距离公式可能被简化为绝对值部分除以sqrt(Nc² + Nr²)。过,在优化的时候,如果参数没有被归一化,分母部分会导致非线性,这可能让优化变得复杂。或者,用户可能希望忽略分母,直接用分子部分作为误差,但这样可能影响结果的准确性,因为实际距离需要考虑分母的模长。过用户的问题中,可能希望将误差定义为e_i = Nc*x_i + Nr*y_i - Dist,即考虑分母,这可能是因为在损失函数中,权的调整或者其他处理方式可以替代。这点需要确认,但根据用户给出的直线方程,可能误差e_i是Nc*x_i + Nr*y_i - Dist,因为方程等于0时,点到直线的距离就是这个值的绝对值除以sqrt(Nc² + Nr²)。但用户是否希望将误差定义为带分母的部分?或者是否在代码实现时已经处理过? 现在,用户的目标函数是L(K) = sum(wi * L(e_i)),其中L是Huber损失函数。所以需要将每个点的误差e_i代入Huber函数,然后乘以权wi,求和得到总损失。 Huber损失函数的公式通常是这样的:当|e| <= delta时,L(e) = 0.5*e²;当|e| > delta时,L(e) = delta*(|e| - 0.5*delta)。这里的delta是一个超参数,控制着从平方损失到线性损失的切换点。 接下来,如何将这个问题转化为非线性最小二乘问题,以使用如Levenberg-Marquardt等优化算法呢?通常,这些算法需要计算残差向量,每个元素对应一个数据点的贡献。对于Huber损失,可能需要新表达目标函数,使其适合迭代优化方法。 但是,Huber损失函数本身是平方项的和,所以直接使用非线性最小二乘优化可能太合适。过,可能可以通过迭代加权最小二乘(IRLS)的方法来处理。因为Huber损失可以视为对每个残差施加同的权,这取决于当前残差的大小。例如,在每次迭代中,根据当前的残差计算权,然后解决一个加权的最小二乘问题,直到收敛。 或者,如果使用一般的非线性优化方法(如梯度下降、牛顿法、LM算法等),需要计算目标函数的梯度和可能的Hessian矩阵。Huber损失的梯度在|e|<=delta时是线性的,在之外是常数。因此,可以构造目标函数的梯度,进而进行优化。 现在,问题中的变量是K = [Nc, Nr, Dist]。需要最小化L(K) = sum(wi * L(e_i)),其中e_i = Nc*x_i + Nr*y_i - Dist。这里可能存在一个问题:直线方程的参数如果加约束,可能存在尺度问题,比如将参数乘以一个常数,导致相同的直线但同的参数值。例如,Nc、Nr、Dist同时乘以一个非零常数,方程代表的直线变,但参数值同。这可能导致优化过程中的参数唯一,使得优化算法难以收敛。因此,可能需要添加约束,比如Nc² + Nr² = 1,或者类似的正则化条件。否则,目标函数可能在参数空间中存在无限多个最小值点,沿着某个方向延伸。但用户给出的待优化变量是这三个参数,所以需要处理这个问题。这可能需要在优化过程中加入约束,或者新参数化变量。 例如,可以采用极坐标的形式,将Nc和Nr表示为cos(theta)和sin(theta),这样自动满足Nc² + Nr² =1。此时,变量变为theta和Dist。或者,在优化时,将参数归一化处理。但用户的问题可能希望保持原来的参数形式,所以可能需要在目标函数中加入正则项,或者调整优化方法,使其能够处理这种尺度问题。 过,用户的问题可能更关注于如何结合Huber损失和优化变量K,所以暂时假设参数是自由的,添加约束,但需要注意这可能带来的问题。 现在,构建目标函数的具体步骤: 1. 对于每个数据点i,计算误差e_i = Nc*x_i + Nr*y_i - Dist。 2. 计算每个点的Huber损失L(e_i)。当|e_i| <= delta时,L(e_i) = 0.5*e_i²;否则,L(e_i) = delta*(|e_i| -0.5*delta)。 3. 目标函数L(K) = sum(wi * L(e_i)),需要最小化这个总和。 为了使用非线性优化方法,需要计算目标函数关于K的梯度。梯度由各个点的贡献组成,每个点的梯度是对K的三个参数的偏导数之和。 计算每个点的梯度: 对于每个点i,梯度分量是wi * dL/de_i * de_i/dK。 其中,dL/de_i在|e_i| <= delta时为e_i;当|e_i| > delta时为delta * sign(e_i)。 而de_i/dK的三个分量分别是x_i(对Nc的偏导)、y_i(对Nr的偏导)、-1(对Dist的偏导)。 因此,每个点的梯度为: 当|e_i| <= delta时,梯度项为 wi * e_i * [x_i, y_i, -1] 当|e_i| > delta时,梯度项为 wi * delta * sign(e_i) * [x_i, y_i, -1] 将这些梯度相加,得到总梯度。 对于Hessian矩阵,可能需要考虑二阶导数,但如果是使用梯度下降或LM算法可能需要显式的Hessian,而是用近似的方法。 现在,如何在代码中实现呢?可能需要使用自动微分库(如TensorFlow或PyTorch)或者数值微分,或者手动计算梯度。但通常,在非线性优化库中,比如scipy.optimize中的minimize函数,可以指定目标函数和梯度,或者使用算法自动估计梯度。 过,用户的问题可能更关注目标函数的数学表达和优化方法的选择,而是具体的代码实现。 此外,权的处理:用户提到的wi是每个点的权可能在数据预处理中给出,或者在优化过程中动态调整(如鲁棒拟合中的迭代加权)。但在这里,wi是作为已知的权可能在数据中存在要性的点。 总结步骤: 1. 定义误差e_i = Nc*x_i + Nr*y_i - Dist。 2. 根据Huber损失计算每个点的损失项。 3. 构建总目标函数为所有点的加权损失之和。 4. 使用非线性优化算法(如LM算法最小化该目标函数,需提供目标函数和梯度。 需要注意的问题: - 参数K的尺度问题,可能导致优化稳定,需要添加约束或新参数化。 - 选择适当的delta值,可能需要交叉验证或其他方法确定。 - 初始化参数K的初始值,可能需要用普通最小二乘的结果作为起点,以提高收敛性。 可能的解决方法: 对于参数归一化问题,将直线参数约束为Nc² + Nr² =1,可以通过将参数表示为theta(角度)和Dist,这样Nc=cos(theta),Nr=sin(theta),同时优化theta和Dist。这可以避免尺度问题,使优化更稳定。但用户给出的变量是K=[Nc, Nr, Dist],所以可能需要这样的参数化处理。否则,当优化过程中Nc和Nr变化时,可能无法正确收敛,因为同样的直线可能对应同的参数组合。 例如,假设初始参数为Nc=1, Nr=0, Dist=5,而另一个参数Nc=2, Nr=0, Dist=10对应的是同一直线。这时候目标函数的值在两种情况下可能同,导致优化问题存在多个解,但实际上这些解代表同一几何直线。因此,参数化的问题需要处理这种冗余。 因此,正确的做法应该是在优化过程中将直线参数归一化,例如每次迭代后都调整Nc和Nr使其满足Nc² + Nr² =1,或者在参数化时直接使用角度和距离。否则,优化问题可能存在多个等价解,导致算法收敛困难或者结果稳定。 因此,在构建目标函数时,可能需要将直线参数归一化,例如将Nc和Nr除以sqrt(Nc² + Nr²),并在优化时仅优化归一化后的参数,或者调整变量。这可能增加问题的复杂性,但可以避免尺度问题。 但用户的问题描述中并没有提到这点,可能需要假设用户希望直接优化这三个参数,而管其比例问题。但这样可能在实际优化中导致问题。例如,当优化过程中Nc和Nr变得很大时,Dist也会相应增大,而误差e_i的值可能变得很大,Huber损失函数的行为可能受到影响。 所以,在优化过程中,是否需要对参数进行归一化处理?这可能一个关键点。如果用户希望直接优化这三个参数,可能需要在目标函数中加入正则化项,例如惩罚Nc² + Nr²,使其趋向于1,或者采用其他方法。 过,这可能超出用户当前的问题范围。用户的问题可能更关注如何结合Huber损失函数构建目标函数,并进行优化,而参数归一化的问题可能需要单独处理。 综上,构建目标函数的具体步骤为: 定义每个点的误差e_i = Nc*x_i + Nr*y_i - Dist 计算每个点的Huber损失:根据e_i的大小选择同的表达式 总目标函数是加权和 优化变量为K = [Nc, Nr, Dist] 梯度计算如上述方法 可能的优化步骤: 1. 初始化K的参数,比如使用普通最小二乘的拟合结果作为初始值。 2. 在每次迭代中,计算所有点的误差,根据当前误差计算Huber损失和梯度。 3. 使用非线性优化算法(如Levenberg-Marquardt、BFGS等)更新参数K,直到收敛。 此外,权的处理:用户提到wi是第i个点的权,这可能是在数据中给定的,或者在鲁棒拟合中通过其他方法动态调整,如IRLS方法中的权更新。但在当前问题中,可能wi是预先确定的,或者在每次迭代中根据残差调整(比如在Huber的情况下,权可以视为对异常值的处理)。但在用户的问题中,权wi可能作为已知的输入,需要在优化过程中调整。 总结来说,用户的最终目标函数是: L(K) = Σ wi * L_huber(e_i) 其中e_i = Nc*x_i + Nr*y_i - Dist 而Huber函数L_huber(e)根据e的大小分段。 为了进行优化,需要计算目标函数及其梯度,并选择合适的优化算法处理可能的参数归一化问题。</think>### 分步解析与目标函数构建 #### 1. **Huber损失函数定义** Huber损失函数结合了均方误差(MSE)和绝对误差(MAE)的优点,公式为: $$ L_\delta(e) = \begin{cases} \frac{1}{2}e^2 & \text{if } |e| \leq \delta \\ \delta(|e| - \frac{1}{2}\delta) & \text{if } |e| > \delta \end{cases} $$ 其中$\delta$为阈值参数,控制对异常值的敏感度。 #### 2. **直线拟合误差定义** 给定直线方程 $N_c x + N_r y - \text{Dist} = 0$,点$(x_i, y_i)$到直线的几何误差为: $$ e_i = \frac{|N_c x_i + N_r y_i - \text{Dist}|}{\sqrt{N_c^2 + N_r^2}} $$ 但在实际优化中,**若对参数归一化**,分母会引入非线性,导致优化复杂度增加。因此,**简化为代数误差**: $$ e_i = N_c x_i + N_r y_i - \text{Dist} $$ 此时,需通过约束$N_c^2 + N_r^2 = 1$保证几何意义一致性。 #### 3. **目标函数构建** 目标是最小化加权Huber损失之和: $$ L(K) = \sum_{i=1}^n w_i \cdot L_\delta(e_i) $$ 其中$K = [N_c, N_r, \text{Dist}]$为待优化变量,$w_i$为权。 #### 4. **梯度计算** 梯度计算分两种情况: - 当$|e_i| \leq \delta$时: $$ \frac{\partial L}{\partial K} = \sum_{i=1}^n w_i \cdot e_i \cdot \frac{\partial e_i}{\partial K} $$ - 当$|e_i| > \delta$时: $$ \frac{\partial L}{\partial K} = \sum_{i=1}^n w_i \cdot \delta \cdot \text{sign}(e_i) \cdot \frac{\partial e_i}{\partial K} $$ 其中,$\frac{\partial e_i}{\partial K} = [x_i, y_i, -1]$。 #### 5. **参数归一化约束** 为避免尺度问题,需约束$N_c^2 + N_r^2 = 1$。常用方法为: - **参数化法**:令$N_c = \cos\theta$, $N_r = \sin\theta$,优化变量变为$\theta$和$\text{Dist}$。 - **正则化项法**:在目标函数中添加惩罚项$\lambda(N_c^2 + N_r^2 - 1)^2$。 #### 6. **优化算法选择** 推荐使用**Levenberg-Marquardt算法**(适合非线性最小二乘)或**BFGS**(需显式梯度),步骤如下: 1. **初始化**:用普通最小二乘估计初始值$K_0$。 2. **迭代更新**:计算梯度及Hessian近似,更新$K$直至收敛。 --- ### 关键代码逻辑(伪代码) ```python import numpy as np from scipy.optimize import minimize def huber_loss(e, delta): return np.where(np.abs(e) <= delta, 0.5*e**2, delta*(np.abs(e) - 0.5*delta)) def huber_gradient(e, delta): return np.where(np.abs(e) <= delta, e, delta * np.sign(e)) def objective(K, points, weights, delta): Nc, Nr, Dist = K e = Nc * points[:,0] + Nr * points[:,1] - Dist return np.sum(weights * huber_loss(e, delta)) def gradient(K, points, weights, delta): Nc, Nr, Dist = K e = Nc * points[:,0] + Nr * points[:,1] - Dist dL_de = huber_gradient(e, delta) dK = np.array([ np.sum(weights * dL_de * points[:,0]), np.sum(weights * dL_de * points[:,1]), -np.sum(weights * dL_de) ]) return dK # 初始化:假设已归一化Nc和Nr K0 = [1, 0, 0] # 示例初始值 result = minimize(objective, K0, args=(points, weights, delta), jac=gradient, method='BFGS') ``` --- ### 注意事项 1. **参数初始化**:建议先用最小二乘拟合初值。 2. **阈值选择**:$\delta$需根据数据噪声调整,通常取残差中位数。 3. **归一化必要性**:若优化稳定,需强制$N_c^2 + N_r^2 =1$。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值