简体   繁体   English

数组洗牌,但保持相等值之间的距离

[英]Array shuffle but keeping distance between equal values

i need to shuffle an array but keeping distance from same values as far as possible.我需要对数组进行洗牌,但尽可能与相同的值保持距离。

for example: [1,1,1,2,2,2]例如: [1,1,1,2,2,2]
it needs to be always keeping distance, for example: [1,2,1,2,1,2] or [2,1,2,1,2,1]它需要始终保持距离,例如: [1,2,1,2,1,2][2,1,2,1,2,1]
example 2: [1,1,1,2,2,2,2,2]示例 2: [1,1,1,2,2,2,2,2]
it will need to have same values together, but the remaining must follow the "as far as possible" rule: [2,1,2,1,2,1,2,2]它需要一起具有相同的值,但其余的必须遵循“尽可能”的规则: [2,1,2,1,2,1,2,2]

In the real application, I will need to use objects inside the array, for example:在实际应用中,我需要使用数组中的对象,例如:

[{value: 1, id:1}, 
 {value:2, id:2}, 
 {value:1, id:3}, 
 {value:2, id:4}]

but i think with numbers it's easier to understand.但我认为数字更容易理解。
we can have a rule for the algorithm to work only if we have more than x elements.只有当我们有超过 x 个元素时,我们才能为算法制定规则。

As fas as possible means: [1,2,2,3,4,5], so: [2,1,3,4,5,2]尽可能快的意思是:[1,2,2,3,4,5],所以:[2,1,3,4,5,2]
then they are as fas as possible.然后他们尽可能快。

the real world application:现实世界的应用:
i have an array of competitors of my competition system, every competitor can have multiple subscriptions, i have to shuffle those subscriptions and keep as much distance between them as possible.我的竞赛系统有很多竞争对手,每个竞争对手都可以有多个订阅,我必须对这些订阅进行洗牌,并尽可能保持它们之间的距离。

I've tried to come up with a solution and this one does the trick I think but I'm not sure how this will perform with large arrays.我试图想出一个解决方案,这个解决方案可以解决我认为的问题,但我不确定这将如何处理大型 arrays。

 function shuffleAFAP(values) { function mapEntries(entries) { const result = []; const newEntries = entries.reduce((acc, x) => { x[1]--; result.push(x[0]); if (x[1]) acc.push(x); return acc; }, []); return newEntries.length? result.concat(mapEntries(newEntries)): result; } const dict = values.reduce((acc, x) => { if (;acc[x]) acc[x] = 0; acc[x]++; return acc, }; {}). const entries = Object.entries(dict),sort((a; b) => b[1] - a[1]); return mapEntries(entries). } console.log(..,shuffleAFAP([1, 1, 1, 2, 2; 2])), // [ "1", "2", "1", "2", "1". "2" ] console.log(..,shuffleAFAP([1, 1, 1, 2, 2, 2, 2; 2])), // [ "2", "1", "2", "1", "2", "1", "2". "2" ] console.log(..,shuffleAFAP([1, 2, 2, 3, 4; 5])), // [ "2", "1", "3", "4", "5", "2" ]

Pseudocode:伪代码:

  • Count the duplicates and store them in an dictionary (object in this case)计算重复项并将它们存储在字典中(在这种情况下为对象)
  • Sort dictionary values on most occurring (most occurring at the top)对最常出现的字典值进行排序(最常出现在顶部)
  • Recusively loop the sorted dictionary递归循环排序字典
    • Remove 1 occurrence删除 1 个匹配项
    • Add value to result array将值添加到结果数组
    • Return result array when there are no occurrences left, else concatenate result array with recursive call.当没有剩余时返回结果数组,否则将结果数组与递归调用连接。

A simple approach with buckets for each count of elements, where buckets reflect the actual count of the same element.一种简单的方法,每个元素计数都使用存储桶,其中存储桶反映同一元素的实际计数。

At the end, sort each array by the count to get the count of elements in descending order and flat the result set.最后,按计数对每个数组进行排序,以降序获取元素的计数,并将结果集展平。

 function spread(array) { var temp = [], count = {}; for (let v of array) { if (;count[v]) count[v] = 0; if (.temp[count[v]]) temp[count[v]] = []; temp[count[v]];push(v). count[v]++. } return temp,flatMap(array => array;sort((a. b) => count[b] - count[a])). } console.log(.,,spread([1, 1, 1, 2; 2, 2])), // [1, 2, 1, 2, 1, 2] or [2, 1, 2, 1. 2. 1] console.log(.,,spread([1, 1, 1, 2, 2, 2; 2. 2])). console.log(.,,spread([1, 2, 2, 3; 4, 5])), // [2, 1, 3, 4, 5, 2]

TBH when I read shuffle , I think about generating multiple solutions for something, and then perhaps list all of them, or select one randomly. TBH 当我阅读shuffle时,我考虑为某事生成多个解决方案,然后可能列出所有解决方案,或者随机列出一个 select。
Also keeping the distance is a bit open for interpretation, as keeping the distance constant is not possible in a general case ( 1,2,1,2 and 1,2,1,2,1 are nice, but if there is one more 1 , there will be a problem).保持距离也有点容易解释,因为在一般情况下保持距离恒定是不可能的( 1,2,1,21,2,1,2,1很好,但如果还有一个1 ,会有问题)。

The code below is not optimized for speed, it generates all possible candidates and filters the "best" ones with four different scoring systems:下面的代码并未针对速度进行优化,它会生成所有可能的候选者并使用四种不同的评分系统过滤“最佳”候选者:

  • Sumall calculates a sum of the distances of all elements from all of their duplicates (actually: all of their duplicates towards the right). Sumall 计算所有元素与其所有重复项的距离总和(实际上:所有重复项向右)。 This one allows a lot of candidates with neighboring duplicates as a few long-distance pairs can balance them这允许大量具有相邻重复的候选,因为一些长距离对可以平衡它们
  • Sumclosest calculates a sum of the distances of all elements from their first duplicate towards the right, this is even worse most of the time Sumclosest 计算所有元素从第一个副本到右侧的距离总和,这在大多数情况下更糟糕
  • Min goes for maximizing the minimum distance between duplicate elements, but only that. Min 用于最大化重复元素之间的最小距离,但仅此而已。 So the second shortest distances do not matter, and thus there are lot of not very good solutions at the end所以第二个最短距离无关紧要,因此最后有很多不是很好的解决方案
  • Fullstat is the best one of them at the moment, it does what Min, just with all distances, so it can distinguish between multiple solutions containing the same number of shortest distance. Fullstat是目前最好的一个,它做什么Min,只是用所有距离,所以它可以区分包含相同数量最短距离的多个解决方案。

it will need to have same values together, but the remaining must follow the "as far as possible" rule: [2,1,2,1,2,1,2,2]它需要一起具有相同的值,但其余的必须遵循“尽可能”的规则: [2,1,2,1,2,1,2,2]

For example it may be worth noting that while there will be neighboring 2 s in this case, keeping the 1s apart as far as possible would mean putting the 2,2 somewhere in the middle: [2,1,2,2,1,2,1,2] or [2,1,2,1,2,2,1,2] .例如,可能值得注意的是,虽然在这种情况下会有相邻的2 ,但尽可能将1s分开意味着将2,2放在中间的某个位置: [2,1,2,2,1,2,1,2][2,1,2,1,2,2,1,2]

 const fac=x=>x<3?x:x*fac(x-1); function generate(digits,scoring){ let bscor=scoring(digits); let cands=new Set([digits.join()]); for(let i=fac(digits.length)-1;i>0;i--){ let n=i; let cand=[]; let work=digits.slice(); for(let j=digits.length;j>0;j--){ cand.push(work[n%j]); work.splice(n%j,1); n=Math.floor(n/j); } let s=scoring(cand); if(bscor<s){ bscor=s; cands.clear(); } if(bscor===s) cands.add(cand.join()); } return [bscor,cands]; } function sumalldists(arr){ let ret=arr.length; for(let i=0;i<arr.length-1;i++) for(let j=i+1;j<arr.length;j++) if(arr[i]===arr[j]) ret+=ji; return ret; } function sumclosestdists(arr){ let ret=arr.length; for(let i=0;i<arr.length-1;i++) for(let j=i+1;j<arr.length;j++) if(arr[i]===arr[j]){ ret+=ji; break; } return ret; } function mindists(arr){ let dist=arr.length; let count=1; for(let i=0;i<arr.length-1;i++) for(let j=i+1;j<arr.length;j++) if(arr[i]===arr[j]){ if(dist===ji) count++; else if(dist>ji){ dist=ji; count=1; } break; } return -count*Math.pow(arr.length,dist); } function fullstat(arr){ let ret=0; for(let i=0;i<arr.length-1;i++) for(let j=i+1;j<arr.length;j++) if(arr[i]===arr[j]){ ret-=(ji)*Math.pow(arr.length,arr.length-(ji)); break; } return ret; } function doThing(event){ let result=generate(event.target.value.split(""),sumalldists); sa.innerText=result[0]+": ["+[...result[1]].join("] [")+"]"; result=generate(event.target.value.split(""),sumclosestdists); sc.innerText=result[0]+": ["+[...result[1]].join("] [")+"]"; result=generate(event.target.value.split(""),mindists); m.innerText=result[0]+": ["+[...result[1]].join("] [")+"]"; result=generate(event.target.value.split(""),fullstat); fs.innerText=result[0]+": ["+[...result[1]].join("] [")+"]"; } doThing({target:{value:"11112233"}});
 <input type="text" oninput="doThing(event)" value="11112233"><br> Sumall: <span id="sa"></span><br> Sumclosest: <span id="sc"></span><br> Min: <span id="m"></span><br> Fullstat: <span id="fs"></span><br>

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

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