我有一个小应用程序,重新计算每个州的国会席位分配,因为用户通过在各州之间移动县来假设地改变人口。功能上有无限的组合,所以我需要动态计算。
方法为fairly straightforward:您为每个州提供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;
}
但是,当每秒调用几次时它会拖一点。我想知道是否有更好的方法将接收座椅的状态放回到排序的阵列中,而不是通过使用整个阵列。我不太了解Javascript sort()
函数以及它是否已经以最大效率完成此操作而不会被告知除了数组中的第一个元素之外的所有元素都已经排序。有没有更有效的方法可以手工实施?
jsFiddle这里:http://jsfiddle.net/raphaeljs/zoyLb9g6/1/
答案 0 :(得分: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次重复的状态(如果需要,还可以重新计算优先级)并取消代表。在第二个中,找到具有最高优先级的状态并添加一个rep(如果需要,还可以重新计算优先级)。
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;
}
}