给定一组没有特定顺序的n个对象(在本例中为n = 5
):
{
apple,
orange,
banana,
cherry,
cabbage
}
我试图通过三个选项向用户提出几个问题,例如:
banana vs. cabbage
(no preference)
在每个问题之后,它会提出一个具有不同选项的新问题(没有偏好保持不变),有效地收集用户偏好的数据。
在几次(本案例中为6或7)问题之后,按顺序给出排名最高的对象的有序列表(数组):
{
cherry,
cabbage,
orange,
apple,
banana
}
但是,我不知道这样的算法是如何工作的,或者它何时知道停止。如果这是一个糟糕的问题,我很抱歉,但我对算法设计还很陌生。
我认为这并不重要,但我使用的是JavaScript。
好的,我四个月后重访了这个,因为我想到了一种新方法来进行排序。
也许,为每个对象设置一个下级列表会更有效率,因此任何不如一个对象的东西都会比它的上级更差。我非常诠释这个,所以这里有一个视觉:
cherry > cabbage
cabbage > banana
cabbage > apple
apple > orange
thus, cherry > cabbage & banana & apple & orange
使用旧方法,得分:
apple vs. orange
apple vs. banana (e.g. apple wins)
apple vs. cherry (e.g. cherry wins)
apple vs. cabbage
orange vs. banana
orange vs. cherry
orange vs. cabbage
banana vs. cherry
banana vs. cabbage
cherry vs. cabbage
10 questions
新方法会使cherry vs. banana
变得多余,因为我们已经知道apple > banana
和cherry > apple
。我真的只需要弄清楚如何编码。
唯一的问题出现在人类循环逻辑(即a > b > c > a
),这是一种可能性,但幸运的是,这对我的特定集合来说不会有问题。
答案 0 :(得分:9)
我前段时间对此进行了调查,作为对相关问题(Collaborative sorting algorithm based on 1 vs 1 choice)的回答的一部分,并发现根据“你更喜欢A还是B?”来创建有序列表?样式问题,使用尽可能少的问题,并且尽管避免循环(如:A> B,B> C,C> A),最好使用二进制插入排序或其变体来完成。
这在实践中意味着您将元素逐个引入有序列表,在列表中找到它们的位置,插入它们,然后转到下一个元素。
要将比较次数减少到最严格的最小值,可以使用二进制插入;这意味着首先将每个新元素与有序列表中间的元素进行比较;这告诉你新元素是在上半部分还是下半部分;然后你将它与那半中间的元素进行比较,依此类推,直到找到它的位置。
作为示例,请考虑需要排序的10个元素的列表。如果您将每个元素与其他元素进行比较,则必须提出45个问题。通过二进制插入,这可以减少到19~25个问题,平均为22.2个问题
确切的问题数量取决于输入:要将1
插入列表[2,4,6,8]
,您要将其与4
进行比较,然后将其与2
进行比较,然后您就可以通过两次比较知道它的位置;要将7
插入列表[2,4,6,8]
,您需要将其与4
进行比较,然后将其与6
进行比较,然后将其与8
进行比较,并仅知道其位置三个比较。通常,插入第n个元素需要log 2 (n)或log 2 (n)+1比较(总是log 2 (n)如果n是2的幂。比较的总数&lt; n.log <子>电子子>(N)。
如果您接受“无偏好”答案,则问题的数量可以更低,如果大多数答案都是“无偏好”,则可以降至n-1。
以下是我为相关问题撰写的Javascript代码段。它问“A还是B?”问题,将“A”,“B”或“无偏好”作为答案,并创建一个有序列表。随机生成器充当给出答案的人。
该算法可以适用于就地对阵列进行排序。首先考虑第一个元素作为排序数组,第二个元素作为要插入的元素,并在必要时交换它们;那么你要考虑前两个元素作为排序列表,第三个元素作为要插入的元素,依此类推。对于二进制插入排序的变体以及减少交换次数的策略,请参阅例如this Wikipedia article
function PrefList(n) {
this.size = n;
this.items = [{item: 0, equals: []}];
this.current = {item: 1, try: 0, min: 0, max: 1};
this.addAnswer = function(x, y, pref) {
if (pref == 0) {
this.items[this.current.try].equals.push(this.current.item);
this.current = {item: ++this.current.item, try: 0, min: 0, max: this.items.length};
} else {
if (pref == -1) this.current.max = this.current.try
else this.current.min = this.current.try + 1;
if (this.current.min == this.current.max) {
this.items.splice(this.current.min, 0, {item: this.current.item, equals: []});
this.current = {item: ++this.current.item, try: 0, min: 0, max: this.items.length};
}
}
}
this.getQuestion = function() {
if (this.current.item >= this.size) return null;
this.current.try = Math.floor((this.current.min + this.current.max) / 2);
return({a: this.current.item, b: this.items[this.current.try].item});
}
this.getOrder = function() {
var index = [];
for (var i in this.items) {
var equal = [this.items[i].item];
for (var j in this.items[i].equals) {
equal.push(this.items[i].equals[j]);
}
index.push(equal);
}
return(index);
}
}
// THIS FUNCTION ACTS AS THE PERSON ANSWERING THE QUESTIONS
function preference(a, b) {
if (Math.random() > 0.6) return -1; else if (Math.random() > 0.333) return 1; else return 0;
}
// CREATE TABLE AND ASK QUESTIONS UNTIL TABLE IS COMPLETE
var fruit = ["orange", "apple", "pear", "banana", "kiwifruit", "grapefruit", "peach", "cherry", "starfruit", "strawberry"];
var t = new PrefList(10), c = 0, q;
while (q = t.getQuestion()) {
document.write(++c + ". " + fruit[q.a] + " or " + fruit[q.b] + "?<BR>");
var answer = preference(fruit[q.a], fruit[q.b]);
document.write(" → " + [fruit[q.a], "no preference", fruit[q.b]][answer + 1] + "<BR>");
t.addAnswer(q.a, q.b, answer);
}
// PERFORM SORT BASED ON TABLE AND DISPLAY RESULT
var index = t.getOrder();
document.write("LIST IN ORDER:<BR>");
for (var i = 0, pos = 1; i < index.length; i++) {
var pre = pos + ". ";
for (var j = 0; j < index[i].length; j++) {
document.write(pre + fruit[index[i][j]] + "<BR>");
pre = " ";
}
pos += index[i].length;
}
答案 1 :(得分:4)
您可以使用sort
:
var sorted = "cherry,cabbage,orange,apple,banana".split(",").sort(function(a,b) {
return prompt([
"If you prefer " + a + ", enter -1",
"If you prefer " + b + ", enter 1",
"If you don't mind , enter 0"
].join("\n"));
});
答案 2 :(得分:1)
我使用angular-js为你的问题写了一个小的jsFiddle(你不需要使用angular)
这个想法是将每个元素与每个其他元素进行比较。将每个元素与其他元素进行比较后,就完成了。在代码中,关注getNext()
函数:
$scope.getNext = function(string) {
if(string == 'one') {
$scope.foodArray[x].score++;
} else {
$scope.foodArray[y].score++;
}
if(y < $scope.foodArray.length -1) {
y++;
$scope.foodTwo = $scope.foodArray[y];
} else if(x < $scope.foodArray.length -2) {
y = 2 + x;
x++;
$scope.foodOne = $scope.foodArray[x];
$scope.foodTwo = $scope.foodArray[y];
} else {
finish();
}
}
前两个if语句用于确定获胜者。
如您所见,我使用变量x和y来存储矩阵中的当前位置。首先,我将食物编号0(= x)与1,2,3,4(= y)进行比较。当y到达array.length-1时,将1加到x并将y设置为x +1。当x到达array.length-2和y array.length-1时,意味着你将所有内容与其他内容进行了比较。现在,您可以按分数对数组进行排序,然后就完成了:)
修改:这是新的Fiddle,增加了回答“无关紧要”的可能性。
还需要考虑的另一件事:在理论上处理偏好时,有三个公理Some explanation:
这些公理必须适用,因此您可以使用utility functions进行计算。
但在实践中尤其是第三个不适用。你会发现有人说:Oranges&gt;苹果,苹果&gt;香蕉但香蕉&gt;桔子强>
在我的小提琴中,我忽略了这些公理,让用户决定他们想要完全合乎逻辑的行为。
答案 3 :(得分:1)
您无需将每个项目与其他项目进行比较。这将要求您向用户询问15个问题以排序5个项目。事实证明,您可以在7次比较中获得5个项目的总排序。
您可以使用sorting network完成此操作。对于任何N,您可以创建一系列比较和交换,以对项目进行排序。已知最佳序列用于分选多达16个项目。除此之外,还有一些算法可以生成排序网络(请参阅链接文章中的构建排序网络)。尽管这些网络并未被证明是最佳的,但它们可能比通用排序算法更有效。
我认为你太容易掩盖的大问题是循环逻辑的真正可能性。在用户偏好方面,传递性通常不成立。
答案 4 :(得分:0)
你可以根据用户的答案为每个项目增加重量。例如,在您的
示例中banana vs. cabbage
(no preference)
当用户选择香蕉时,您可以在香蕉项目中添加加号。如果他们选择卷心菜你加一个卷心菜,然后如果他们选择没有偏好,那么你可以只给两个加号。然后,您可以将列表从最大值排序到最小值。
答案 5 :(得分:0)
使用地图(javascript对象可以这样做),将object作为键和整数(最初为所有对象设置为0)作为值。
对于每个问题,您都会增加相应所选键的值。由于没有任何偏好影响它们,所以如果选择它就无需做任何事情。
然后迭代地图,找到具有最高值的键值对。退还钥匙。
答案 6 :(得分:0)
将所有项目保留在数组中。对于对之间的关系使用单独的数组/散列映射。然后使用您最喜欢的基于比较的搜索算法(比如quicksort)。
当要比较两个元素时,使用地图找出相对优先级。如果没有已知优先权,请询问用户。
添加新优先级时,请调整整个矩阵的优先顺序。例如,当用户说苹果&lt;香蕉然后分开香蕉&lt;樱桃,添加苹果&lt;樱桃到哈希地图。
基于比较的搜索算法旨在优化比较次数,这可以转化为在这种情况下向用户提出的问题数量的优化。
答案 7 :(得分:0)
将元素保存在这样的数组中:
var preferences = [
{
"name": "banana",
"predecessors": []
},
{
"name": "orange",
"predecessors": []
},
{
"name": "cabbage",
"predecessors": []
},
];
现在,当用户选择他们的首选项时,将前任添加到元素中。让我们说用户将香蕉放在橙子上,将卷心菜放在橙色上,将卷心菜放在香蕉上。结果:
var preferences = [
{
"name": "banana",
"predecessors": ["orange"]
},
{
"name": "orange",
"predecessors": []
},
{
"name": "cabbage",
"predecessors": ["orange", "banana"]
},
];
现在使用自定义比较器函数对数组进行排序:
preferences.sort(function(a,b) {
if(b.predecessors.contains(a.name)) {
return -1;
}
if(a.predecessors.contains(b.name)) {
return 1;
}
return 0;
});
请注意,javascript没有原生&#34;包含&#34;数组的函数。您可以使用jQuery中的$ .inArray或下划线中的_.contains,或者自己编写一个。
答案 8 :(得分:0)
简单说明:
var options = {
A: 0,
B: 0,
C: 0,
D: 0
};
var optionsNames = Object.keys(options);
var results = document.getElementById('results');
function printResults() {
var res = optionsNames.map(function(option) {
return option + ': ' + options[option];
});
results.innerHTML = res.join('<br>');
}
function getNextQuestion() {
var unclear = [];
optionsNames.sort(function(a, b) {
if (options[a] == options[b]) unclear.push([a, b]);
else return options[b] - options[a];
return 0;
});
return unclear[0];
}
var next;
function choose(nextIdx) {
if (next && next[nextIdx] && options[next[nextIdx]] !== undefined) {
options[next[nextIdx]] += 1;
}
console.log('click', options, next[nextIdx]);
updateView();
}
var btn1 = document.getElementById('one');
var btn2 = document.getElementById('two');
function updateView() {
next = getNextQuestion();
if (next) {
btn1.innerHTML = next[0];
btn2.innerHTML = next[1];
} else {
btn1.innerHTML = 'FINISH';
btn2.innerHTML = 'FINISH';
}
printResults();
}
updateView();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="one" onclick="choose(0)"></button> vs. <button id="two" onclick="choose(1)"></button>
<div id="results"></div>
答案 9 :(得分:0)
尝试创建具有选择名称的项目数组,使用input type="radio"
元素,删除选定的元素,将值推送到对象的三个属性之一。对象的属性1
将包含一个项,属性2
将包含输入数组长度/ 3项,属性3
将包含剩余的选择;当一个选择选项保留在form
中时,将提供“无”。
var arr = [
"apple",
"orange",
"banana",
"cherry",
"cabbage"
];
var data = {
"1": [],
"2": [],
"3": []
};
var form = document.forms["choices"];
var html = arr.map(function(val, index) {
var input = document.createElement("input");
var label = document.createElement("label");
label.textContent = val + ":";
input.type = "radio";
input.name = "choice";
input.className = "choices";
input.value = val;
label.appendChild(input);
return label.outerHTML
});
form.lastElementChild
.insertAdjacentHTML("beforebegin", html.join("") + "<br>");
form.onchange = function(e) {
e.preventDefault();
if (data[1].length + data[2].length + data[3].length < arr.length - 2) {
if (data[1].length === 0) {
data[1].push(e.target.value);
} else {
if (data[2].length < arr.length / 3) {
data[2].push(e.target.value);
} else {
data[3].push(e.target.value);
}
}
form.removeChild(e.target.parentElement);
} else {
if (data[1].length + data[2].length + data[3].length === arr.length - 2) {
data[3].push(e.target.value);
form.removeChild(e.target.parentElement);
var input = document.createElement("input");
var label = document.createElement("label");
label.textContent = "none";
input.type = "radio";
input.name = "choice";
input.className = "choices";
input.value = "none";
label.appendChild(input);
form.getElementsByClassName("choices")[0].parentElement
.insertAdjacentHTML("afterend", label.outerHTML)
} else {
data[3].push(e.target.value);
form.removeChild(e.target.parentElement);
form.removeChild(form.getElementsByClassName("choices")[0].parentElement);
form.firstElementChild.innerHTML = "Results:";
form.querySelector("pre").textContent = JSON.stringify(data, null, 2)
}
}
console.log(e.target.value, data);
}
<form name="choices">
<span>Select one:</span>
<br>
<pre name="result"></pre>
</form>