给定一个整数数组,以近似相等的距离调整数组大小

时间:2017-01-11 17:05:07

标签: arrays algorithm sampling

给出一系列这样的数字:

[0, 99, 299, 498, 901]

我可以使用什么算法将该数组的大小调整为大小相等的数组。或者换句话说,重新采样具有近似最大公倍数。因此,通过上面的示例,最大公约数大约 100,结果将是:

[0, 99, 200, 299, 400, 498, 600, 700, 800, 901]

使用原始值会很好,并且可以设置错误栏(上面的解决方案将错误设置为2),但也会对此结果感到满意:

[0, 100, 200, 300, 400, 500, 600, 700, 800, 900]

2017年1月12日更新

根据Redu的回答,这是他的代码的Swift版本:

var arr: [Int] = [0, 99, 299, 498, 901]

var diffs = [Int]()
var minGap: Int = 0
for x in 0..<arr.count-1 {
    let gap = arr[x+1] - arr[x]
    diffs.append(gap)
    if minGap == 0 || minGap > gap {
        minGap = gap
    }
}

var resamples = [Int]()
for item in arr {
    if let lastSample = resamples.last {
        let n = Int((Float(item - lastSample) / Float(minGap)).rounded())
        let g = (item - lastSample) / n
        var inserts = [Int]()
        for x in 0..<n-1 {
            let newSample = lastSample + ((x+1) * g)
            inserts.append(newSample)
        }
        resamples.append(item)
        resamples.append(contentsOf: inserts)
    } else {
        resamples.append(item)
    }
}

2 个答案:

答案 0 :(得分:2)

基本上,您希望对算术级数使用最小二乘回归。

算术级数可以用3个术语参数化:第一学期,最后一学期和共同学习。这3个术语将形成您的目标函数的参数,您将寻求最小化。

在每个优化步骤中,您需要选择试验算术级数中的哪些术语需要针对原始集合进行回归。这将是非常具有挑战性的,但幸运的是两个系列都将被排序,所以这应该是O(N)遍历。

围绕3个术语的约束将是一个印刷上令人愉悦的集合。例如,100,200,300优先于99,198,297,即使源系列是99,297?

一个完整的答案,我觉得太宽泛 - 可能至少一个星期的工作。但这就是我开始这个项目的方式。

答案 1 :(得分:1)

以下是我在JS中的解决方案。我首先找到最小间隙,然后尝试在不改变原始值的情况下相应地找出每个项目和过程之间的差异。

显然,要使此算法正常工作,输入数组必须按升序排序。

&#13;
&#13;
var arr = [0, 99, 299, 498, 901],
    gap = Math.min(...Array(arr.length-1).fill().map((_,i) => arr[i+1]-arr[i])),  // Find the minimum gap
    res = arr.reduce((p,c,i) => { var n = Math.round((c-p[p.length-1])/gap);      // Find howmany gaps are inbetween according to the minimum gap
                                      g = Math.round((c-p[p.length-1])/n);        // Calculate the average gap top apply
                                  return i ? p.concat(Array(Math.round(n-1)).fill().map((_,i) => p[p.length-1] + (i+1)*g),c)
                                           : p.concat(c);
                                },[]);
console.log(res);
&#13;
&#13;
&#13;

<强>解释

gap = Math.min(...Array(arr.length-1).fill().map((_,i) => arr[i+1]-arr[i])),

首先,我们设置一个大小比输入数组小一个的新数组。 (Array(arr.length-1))首先我们使用未定义的元素初始化(.fill()),然后.map()使用arr[i+1]-arr[i]初始化每个元素。所以现在我们有了gap数组。然后我们将它作为参数传播到Math.min()函数中。它是Math.min(...Array(部分。所以现在我们在上述情况下的最小差距为99.

res = arr.reduce((p,c,i) => { var n = Math.round((c-p[p.length-1])/gap);
                                  g = Math.round((c-p[p.length-1])/n);
                              return i ? p.concat(Array(Math.round(n-1)).fill().map((_,i) => p[p.length-1] + (i+1)*g),c)
                                       : p.concat(c);
                            },[]);

.reduce()部分看起来有点难看但很容易。我们的.reduce()操作接受一个函数作为它的参数(通常称为回调函数),并在数组项的每次迭代中运行它。此回调函数是以(p,c,i) => {... }开头的部分。这是一个箭头功能。这与正常功能基本相同。 x => x表示function(x) { return x;}x => {return x;}。在我们的例子中,因为我们使用大括号来定义函数的主体(由于多个语句),我们将不得不使用return指令。

我们的.reduce()使用的初始值为空数组。它是最后的,[]);部分。将为每个数组项调用reduce的回调函数将传递三个参数(p,c,i)初始空数组被赋值给p(previous)参数,当前项被赋值给{{1参数和当前索引被分配给每次调用的c参数。

在我们的回调体中,我们定义了2个变量。 in

g

n = Math.round((c-p[p.length-1])/gap); 返回p[p.length-1]数组的最后一个元素。所以在第一轮;当p = 0时,ip[0]undefinedMath.round((c-p[p.length-1])/gap);(非数字),但我们不在乎,因为;

NaN

三元条件意味着;

return i ? p.concat(Array(Math.round(n-1)).fill().map((_,i) => p[p.length-1] + (i+1)*g),c)
         : p.concat(c);

因此,根据条件,您会看到其中一条指令并返回结果。在我们的示例中,结果将作为result = condition ? if true do this : if false do this 的值返回。

因此,在我们的情况下,如果p == 0(JS中的i值),则只执行false并返回新的p.concat(c)值并继续下一次迭代(使用新的ppc值调用回调。

如果i不是i(0以外的任何值),那么

false

这意味着创建一个大小的数组以获取许多临时元素的间隙,使用p.concat(Array(Math.round(n-1)).fill().map((_,i) => p[p.length-1] + (i+1)*g),c) 初始化数组并使用undefineds映射每个元素并将此数组连接到p[p.length-1] + (i+1)*g数组并将p追加到最后,然后返回c数组。

有一点需要提醒:p指令会返回一个新数组,其中包含p.concat(whatever...)和&#34; items&#34;的元素。作为参数包含的数组或包含ar参数的项本身。我的意思是;

p会产生[1,2,3].concat([4,5,6],[7,8],9)

所以这应该解释一下。