HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptMinor

Optimising a user's-choice sorting algorithm

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
sortinguseroptimisingalgorithmchoice

Problem

With the help of some nice folks at Stack Overflow, I wrote an algorithm that will give a user three buttons: option a neither option b Clicking on a button will add one to the score of a object, and it will systematically go through each match-up to get an equal amount of questions for each character.

Here's the meat of the function:

var pos1 = 0; //position in the array
var pos2 = 1; //ditto

var array = [ //array used
    {name: "apple", score: 0},
    {name: "pear", score: 0},
    {name: "cherry", score: 0},
    {name: "banana", score: 0},
    {name: "orange", score: 0},
    {name: "watermelon", score: 0},
    {name: "bread" score: 0},
    {name: "a duck" score: 0}
]

var option1 = array[0]; //keeps track of which option is shown in the button
var option2 = array[1]; //ditto

function select(slot) { //called when user presses button
    if (slot == 1) { //left button
        array[pos1].score++;
    } else if (slot == 2) { //right button
        array[pos2].score++;
    } //for neither, it does select(0)

    if (pos2 < array.length - 1) {
        pos2++;
        option2 = array[pos2];
    } else if (pos1 < array.length - 2) {
        pos1++;
        pos2 = 1 + pos1;
        option1 = array[pos1]
        option2 = array[pos2]
    } else {
        output(); //sorts and displays
        return;
    }
}


It works pretty well for smaller numbers of objects, but it gets totally absurd at higher numbers. For example, in practice, one of my sets has around 50 objects, resulting in ~1500 questions, which takes hours!

As a side note, I found this website. Ignoring the content, it's able to sort a similar number (55) of objects with only 170 questions. It's also somehow able to generate a percentage of completion, but that's just a bonus.

Conveniently, the author has the code embedded directly in the HTML, so the JavaScript is easy to view. However, I can't figure out how it works, but it's the type of efficiency I'm looking for.

Solution

You'll have to excuse my javascript, which is kind of lacking, but you can use any comparison based algorithm to make the sorting happen in \$O(n \log n)\$ comparisons, where a comparison is one of your user powered tests. I think merge sort is guaranteed to need exactly \$n \log n\$, and is relatively simple to implement, so you could implement a bottom-up mergesort by doing something along the lines of:

var array = ["apple", "pear", "cherry",  "banana", "orange", "watermelon",
             "bread", "a duck"];
var temp = [];

var merge_len = 1
var merge_lo = 0;
var merge_mid = 1;
var merge_hi = 2;
var merge_ptr_1 = 0;
var merge_ptr_2 = 1;

function mergesort_step(slot) {
    if (slot == 2) { // right button pressed
        temp.push(array[merge_ptr_2]);
        merge_ptr_2++;
    }
    else {
        temp.push(array[merge_ptr_1]);
        merge_ptr_1++;
    }

    if (merge_ptr_1 == merge_mid || merge_ptr_2 == merge_end) {
        // Copy any remaining items in 1st merged subarray to temp
        for (i = merge_ptr_1; i = array.length) {
            merge_lo = 0;
            merge_len *= 2;
            if (merge_len >= array.length) {
                // We are finished!
                output();
                return
            }
        }
        else {
            merge_lo = merge_hi;
        }
        merge_mid = merge_lo + merge_len;
        merge_hi = merge_mid + merge_len;
        if (merge_hi > array.length) {
            merge_hi = array.length();
        }
        merge_ptr_1 = merge_lo;
        merge_ptr_2 = merge_mid;
    }
}


Here merge_ptr_1 and merge_ptr_2 have taken the place of your pos1 and pos2, and I have skipped your option1 and option2 entirely to not bloat the code up any more.

Code Snippets

var array = ["apple", "pear", "cherry",  "banana", "orange", "watermelon",
             "bread", "a duck"];
var temp = [];

var merge_len = 1
var merge_lo = 0;
var merge_mid = 1;
var merge_hi = 2;
var merge_ptr_1 = 0;
var merge_ptr_2 = 1;

function mergesort_step(slot) {
    if (slot == 2) { // right button pressed
        temp.push(array[merge_ptr_2]);
        merge_ptr_2++;
    }
    else {
        temp.push(array[merge_ptr_1]);
        merge_ptr_1++;
    }

    if (merge_ptr_1 == merge_mid || merge_ptr_2 == merge_end) {
        // Copy any remaining items in 1st merged subarray to temp
        for (i = merge_ptr_1; i < merge_mid; merge_ptr_1++) {
            temp.push(array[merge_ptr_1]);
        }
        // Copy any remaining items in 2nd merged subarray to temp
        for (i = merge_ptr_2; i < merge_hi; merge_ptr_2++) {
            temp.push(array[merge_ptr_2]);
        }
        // Overwrite the two subarrays with the sorted temp
        for (i = 0; i < merge_hi - merge_lo; i++) {
            array[i + merge_lo] = temp[i];
        }
        temp = []
        // Set up the next two subarrays to merge
        if (merge_hi + merge_len >= array.length) {
            merge_lo = 0;
            merge_len *= 2;
            if (merge_len >= array.length) {
                // We are finished!
                output();
                return
            }
        }
        else {
            merge_lo = merge_hi;
        }
        merge_mid = merge_lo + merge_len;
        merge_hi = merge_mid + merge_len;
        if (merge_hi > array.length) {
            merge_hi = array.length();
        }
        merge_ptr_1 = merge_lo;
        merge_ptr_2 = merge_mid;
    }
}

Context

StackExchange Code Review Q#97969, answer score: 2

Revisions (0)

No revisions yet.