简体   繁体   English

仅更改第一个值时如何求助Javascript数组

[英]How to resort a Javascript array when only the first value has changed

I've got a little app that recalculates the apportionment of seats in Congress in each state as the user changes the population hypothetically by moving counties between states. 我有一个小应用程序,当用户通过在州之间移动县来假设地改变人口时,该应用程序可以重新计算每个州在国会中的席位分配。 There are functionally infinite combinations, so I need to compute this on the fly. 在功能上有无限的组合,因此我需要即时进行计算。

The method is fairly straightforward : You give each state 1 seat, then assign the remaining 385 iteratively by weighting them according to population / ((seats * (seats + 1)) and assigning the seat to the top priority state. 该方法非常简单 :给每个州分配1个席位,然后通过根据population / ((seats * (seats + 1))加权权重,将剩余的385个州迭代分配,并将该席位分配给最高优先级州。

I've got this working fine the obvious way: 我以明显的方式使此工作正常:

function apportion(states) {
    var totalReps = 435;

    // assign one seat to each state
    states.forEach(function(state) {
        state.totalReps = 1;
        totalReps -= 1;
        state.priority = state.data.population / Math.sqrt(2); //Calculate default quota
    });

    // sort function
    var topPriority = function(a, b) {
        return b.priority - a.priority;
    };

    // assign the remaining 385
    for (totalReps; totalReps > 0; totalReps -= 1) {
        states.sort(topPriority);    
        states[0].totalReps += 1;
        // recalculate the priority for this state
        states[0].priority = states[0].data.population / Math.sqrt(states[0].totalReps * (states[0].totalReps + 1));
    }    
    return states;
}

However, it drags a little when called several times a second. 但是,每秒调用几次时,它会拖一点。 I'm wondering whether there's a better way to place the state that received the seat back in the sorted array other than by resorting the whole array. 我想知道是否有比通过整个数组更好的方法来将接收到座位的状态放到排序后的数组中。 I don't know a ton about the Javascript sort() function and whether it will already do this with maximal efficiency without being told that all but the first element in the array is already sorted. 我不了解Java的sort()函数及其是否会以最大的效率做到这一点,而又不知道数组中除第一个元素外的所有其他元素都已被sort()对此我一无所知。 Is there a more efficient way that I can implement by hand? 有没有我可以手动实施的更有效的方法?

jsFiddle here: http://jsfiddle.net/raphaeljs/zoyLb9g6/1/ jsFiddle在这里: http : //jsfiddle.net/raphaeljs/zoyLb9g6/1/

Using a strategy of avoiding sorts, the following keeps an array of priorities that is aligned with the states object and uses Math.max to find the highest priority value, then indexOf to find its position in the array, then updates the states object and priorities array. 使用避免排序的策略,以下代码将保留与状态对象对齐的优先级数组 ,并使用Math.max查找最高优先级值,然后使用indexOf查找其在数组中的位置,然后更新状态对象和优先级阵列。

As with all performance optimisations, it has very different results in different browsers (see http://jsperf.com/calc-reps ), but is at least no slower (Chrome) and up to 4 times faster (Firefox). 与所有性能优化一样,它在不同的浏览器中具有截然不同的结果(请参阅http://jsperf.com/calc-reps ),但至少不慢(Chrome)和快4倍(Firefox)。

function apportion1(states) {
    var totalReps = 435;
    var sqrt2 = Math.sqrt(2);
    var priorities = [];
    var max, idx, state, n;

    // assign one seat to each state
    states.forEach(function(state) {
        state.totalReps = 1;
        state.priority = state.data.population / sqrt2; //Calculate default quota
        priorities.push(state.priority);
    });

    totalReps -= states.length;

    while (totalReps--) {
      max = Math.max.apply(Math, priorities);
      idx = priorities.indexOf(max);
      state = states[idx];
      n = ++state.totalReps;
      state.priority = state.data.population / Math.sqrt(n * ++n);
      priorities[idx] = state.priority;
    }
    return states;
}

For testing I used an assumed states object with only 5 states, but real population data. 为了进行测试,我使用了一个假设状态对象,该对象仅包含5个状态,但包含实际人口数据。 Hopefully, with the full 50 states the benefit will be larger. 希望在全部50个州中受益会更大。

Another strategy is to sort on population since that's how the priorities are distributed, assign at least one rep to each state and calculate the priority, then run from 0 adding reps and recalculating priorities. 另一种策略是对人口进行排序,因为这是优先级的分配方式,为每个州分配至少一个代表并计算优先级,然后从0开始增加代表并重新计算优先级。 There will be a threshold below which a state should not get any more reps. 将有一个阈值,低于该阈值状态将不再获得任何代表。

Over to you. 交给你。 ;-) ;-)

Edit 编辑

Here's a really simple method that apportions based on population. 这是一个非常简单的基于人口进行分配的方法。 If may allocation one too many or one too few. 如果可能分配一个或多个。 In the first case, find the state with the lowest priority and at least 2 reps (and recalc priority if you want) and take a rep away. 在第一种情况下,找到具有最低优先级和至少2次重复的状态(如果需要,还可以重新计算优先级),然后带走代表。 In the second, find the state with the highest priority and add one rep (and recalc priority if required). 在第二个中,找到优先级最高的状态并添加一个代表(并根据需要重新计算优先级)。

function simple(states) {
  var totalPop = 0;
  var totalReps = 435
  states.forEach(function(state){totalPop += state.data.population});
  var popperrep = totalPop/totalReps;
  states.forEach(function(state){
    state.totalReps = Math.round(state.data.population / popperrep);
    state.priority = state.data.population / Math.sqrt(state.totalReps * (state.totalReps + 1));
  });
  return states;
}

Untested, but I'll bet it's very much faster than the others. 未经测试,但我敢打赌,它比其他方法快得多。 ;-) ;-)

I've updated the test example for the simple function to adjust if the distribution results in an incorrect total number of reps. 我已经更新了简单功能的测试示例,以调整分布是否导致错误的总次数。 Tested across a variety of scenarios, it gives identical results to the original code even though it uses a very different algorithm. 经过多种场景的测试,即使使用了非​​常不同的算法,它也可以为原始代码提供相同的结果。 It's several hundred times faster than the original with the full 50 states. 它比具有50个州的原始州快数百倍。

Here's the final version of the simple function: 这是简单功能的最终版本:

function simple(states) {
  var count = 0;
  var state, diff;
  var totalPop = states.reduce(function(prev, curr){return prev + curr.data.population},0);
  var totalReps = 435
  var popperrep = totalPop/totalReps;

  states.forEach(function(state){
    state.totalReps = Math.round(state.data.population / popperrep) || 1;
    state.priority = state.data.population / Math.sqrt(state.totalReps * (state.totalReps + 1));
    count += state.totalReps;
  });

  // If too many reps distributed, trim from lowest priority with 2 or more
  // If not enough reps distributed, add to highest priority
  while ((diff = count - totalReps)) {
    state = states[getPriority(diff < 0)];
    state.totalReps += diff > 0? -1 : 1;
    count += diff > 0? -1 : 1;
    state.priority = state.data.population / Math.sqrt(state.totalReps * (state.totalReps + 1));
    // console.log('Adjusted ' + state.data.name + ' ' + diff);
  }

  return states;

  // Get lowest priority state with 2 or more reps,
  // or highest priority state if high is true
  function getPriority(high) {
    var idx, p = high? 0 : +Infinity;
    states.forEach(function(state, i){

      if (( high && state.priority > p) || (!high && state.totalReps > 1 && state.priority < p)) {
        p = state.priority;
        idx = i;
      }
    });
    return idx;
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM