[英]How to resort a Javascript array when only the first value has changed
我有一个小应用程序,当用户通过在州之间移动县来假设地改变人口时,该应用程序可以重新计算每个州在国会中的席位分配。 在功能上有无限的组合,因此我需要即时进行计算。
该方法非常简单 :给每个州分配1个席位,然后通过根据population / ((seats * (seats + 1))
加权权重,将剩余的385个州迭代分配,并将该席位分配给最高优先级州。
我以明显的方式使此工作正常:
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;
}
但是,每秒调用几次时,它会拖一点。 我想知道是否有比通过整个数组更好的方法来将接收到座位的状态放到排序后的数组中。 我不了解Java的sort()
函数及其是否会以最大的效率做到这一点,而又不知道数组中除第一个元素外的所有其他元素都已被sort()
对此我一无所知。 有没有我可以手动实施的更有效的方法?
jsFiddle在这里: http : //jsfiddle.net/raphaeljs/zoyLb9g6/1/
使用避免排序的策略,以下代码将保留与状态对象对齐的优先级数组 ,并使用Math.max查找最高优先级值,然后使用indexOf查找其在数组中的位置,然后更新状态对象和优先级阵列。
与所有性能优化一样,它在不同的浏览器中具有截然不同的结果(请参阅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;
}
为了进行测试,我使用了一个假设状态对象,该对象仅包含5个状态,但包含实际人口数据。 希望在全部50个州中受益会更大。
另一种策略是对人口进行排序,因为这是优先级的分配方式,为每个州分配至少一个代表并计算优先级,然后从0开始增加代表并重新计算优先级。 将有一个阈值,低于该阈值状态将不再获得任何代表。
交给你。 ;-)
这是一个非常简单的基于人口进行分配的方法。 如果可能分配一个或多个。 在第一种情况下,找到具有最低优先级和至少2次重复的状态(如果需要,还可以重新计算优先级),然后带走代表。 在第二个中,找到优先级最高的状态并添加一个代表(并根据需要重新计算优先级)。
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;
}
未经测试,但我敢打赌,它比其他方法快得多。 ;-)
我已经更新了简单功能的测试示例,以调整分布是否导致错误的总次数。 经过多种场景的测试,即使使用了非常不同的算法,它也可以为原始代码提供相同的结果。 它比具有50个州的原始州快数百倍。
这是简单功能的最终版本:
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.