重新缩放整数向量

时间:2011-05-16 16:48:07

标签: algorithm math vector integer computer-science

假设我有一个正整数的向量V.如果整数之和大于正整数N,我想重新缩放V中的整数,使得和为< = N.V中的元素必须保持在零以上。 V的长度保证为< = N。

是否有算法在线性时间内执行此重新缩放?

这不是家庭作业,BTW :)。我需要从符号到符号频率重新缩放地图以使用范围编码。

一些快速思考和谷歌搜索没有解决问题。

编辑:

好的,这个问题有点不清楚。 “重新缩放”表示“标准化”。也就是说,将V中的整数(例如通过将它们乘以常数)变换为较小的正整数,从而满足sum(V)<= N的标准。保留整数之间的比率越好,压缩效果越好。

问题是以这种方式开放式的,该方法不需要找到保持比率的最佳(例如,最小二乘拟合感)方法,而是“好”的方法。如建议的那样,将整个向量设置为1是不可接受的(除非强制)。例如,“好”足以找到满足和标准的最小除数(定义如下)。

以下天真算法不起作用。

  1. 找到当前总和(V),Sv
  2. divisor:= int(ceil(Sv / N))
  3. 用除数除以V中的每个整数,向下舍入,但不得小于1.
  4. 在v = [1,1,1,10]时失败,N = 5。

    divisor = ceil(13 / 5) = 3.
    V := [1,1,1, max(1, floor(10/3)) = 3]
    Sv is now 6 > 5.
    

    在这种情况下,正确的归一化是[1,1,1,2]

    一种可行的算法是对除数(上面定义)进行二元搜索,直到找到满足求和标准的[1,N]中的最小除数。从ceil(Sv / N)开始猜测。但是,这不是线性的操作数,而是与len(V)* log(len(V))成比例。

    在一般情况下,我开始认为在线性时间内做不好。我可能会采用某种启发式方式。

4 个答案:

答案 0 :(得分:4)

将所有整数除以Greatest Common Divisor。您可以使用Euclid's Algorithm的多个应用程序有效地找到GCD。

d = 0
for x in xs:
    d = gcd(d, x)

xs = [x/d for x in xs]

积极的一点是,你总是以这种方式尽可能小的表现形式,不会丢掉任何精确度,也不需要选择特定的N.缺点是,如果你的频率是大的互质数字,你将别无选择,牺牲精确度(并没有说明在这种情况下应该做什么)。

答案 1 :(得分:1)

我认为你应该重新调整高于1的部分。因此,从所有值中减去1,从N中减去V.length。然后正常重新缩放,然后再加1。如果你继续运行总计,你甚至可以做得更好,而不是只选择一个因素,这通常会浪费一些“数字空间”。像这样:

public static void rescale(int[] data, int N) {
    int sum = 0;
    for (int d : data)
        sum += d;

    if (sum > N) {
        int n = N - data.length;
        sum -= data.length;

        for (int a = 0; a < data.length; a++) {
            int toScale = data[a] - 1;
            int scaled = Math.round(toScale * (float) n / sum);

            data[a] = scaled + 1;
            n -= scaled;
            sum -= toScale;
        }
    }
}

答案 2 :(得分:1)

这个怎么样:

  1. 找到当前总和(V),Sv
  2. divisor:= int(ceil(Sv /(N - | V | + 1))
  3. 用除数除以V中的每个整数,向上舍入
  4. 在v = [1,1,1,10]上,N = 5:

    divisor = ceil(13/2)= 7。 V:= [1,1,1,ceil(10/7))= 2]

答案 3 :(得分:0)

这是“范围归一化”的问题,但它很容易。假设S是矢量的元素之和,并且S> = N,则S = dN,对于某些d> = 1。因此d = S / N.因此,只需将矢量的每个元素乘以N / S(即除以d)。结果是一个带有重新缩放分量的向量,其总和恰好是N.这个过程显然是线性的:)