【问题标题】:Given an array of integers, resize the array with approximate equal distances给定一个整数数组,用近似相等的距离调整数组的大小
【发布时间】:2017-01-11 17:05:06
【问题描述】:

给定一个这样的数字数组:

[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)
    }
}

【问题讨论】:

  • 大概你希望结果被四舍五入。即 100、200、300 而不是 99、198、297。
  • 嗯,我想我可以四舍五入到首选的错误级别。所以如果我四舍五入到最接近的 10,最小的 GCD 值将是 10。

标签: arrays algorithm sampling


【解决方案1】:

基本上你想对算术级数使用最小二乘回归。

算术级数可以用 3 个项参数化:第一项、最后一项和共同差。这 3 个术语将构成您的目标函数的参数,您将寻求将其最小化。

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

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

一个完整的答案我觉得太宽泛了 - 可能至少需要一周的时间。但这就是我开始这个项目的方式。

【讨论】:

    【解决方案2】:

    以下将是我在 JS 中的解决方案。我首先找到最小间隙,然后尝试找出其中有多少适合每个项目,并在不改变原始值的情况下进行相应处理。

    显然,要使该算法起作用,输入数组必须按升序排序。

    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);

    说明:

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

    首先,我们设置一个大小比输入数组小一的新数组。 (Array(arr.length-1)) 首先我们用未定义的元素初始化 (.fill()) 它,然后用arr[i+1]-arr[i] 初始化每个元素.map()。所以现在我们有了 gaps 数组。然后我们将它作为参数传播到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) =&gt; {... }开头的部分。这是一个箭头函数。这与正常功能基本相同。 x =&gt; x 表示 function(x) { return x;}x =&gt; {return x;}。在我们的例子中,由于我们使用大括号来定义函数的主体(由于多个语句),我们将不得不使用 return 指令。

    我们的.reduce() 使用一个空数组的初始值。这是最后的,[]); 部分。回调函数,reduce 将调用每个数组项,将传递三个参数(p,c,i) 初始空数组分配给p(上一个)参数,当前项分配给c 参数和当前每次调用都会将索引分配给 i 参数。

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

    n = Math.round((c-p[p.length-1])/gap);
    

    p[p.length-1] 返回p 数组的最后一个元素。所以在第一回合;当i = 0,p[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 的值返回。

    所以在我们的例子中,如果i == 0(JS 中的false 值)则只执行p.concat(c) 并返回新的p 值并继续下一次迭代(使用新的@987654360 调用回调@、ci 值。

    如果i 不是false(0 以外的任何值),那么就做喜欢

    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...) 指令将返回一个新数组,该数组由p 的元素和作为参数包含的数组的“项”或包含在 ar 参数中的项本身组成。我的意思是;

    [1,2,3].concat([4,5,6],[7,8],9) 将导致 [1,2,3,4,5,6,7,8,9]

    所以这应该解释它。

    【讨论】:

    • 这是一个不错的解决方案。我认为这比找到我最初的想法所暗示的 GCD 更有效。
    • @elprl 谢谢..我想在这里批评自己...再次阅读我的答案,最后一句话对我来说没有多大意义:) Math.abs(c-p[p.length-1]) 应该足以也可以处理未排序的数组。
    • 我不得不说 JS 是相当棘手的。我不是 JS 专家,但我是 Swift 专家,我在执行“return i”行并转换该代码时遇到了麻烦。你能解释一下那里发生了什么吗?
    • @elpri 这里是清晨。今天晚些时候,我将在我的答案下详细解释代码。简而言之,gap 是最小距离,res 只是一个 JS 函数式 reduce 指令,它接受一个函数并将其应用于数组的每个元素。在这种情况下,它适用于 arr 数组。给我几个小时,我会提供详细信息。
    • @elpri OK 添加了解释,希望对您有所帮助。 :)
    猜你喜欢
    • 2012-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-10
    • 1970-01-01
    相关资源
    最近更新 更多