views:

1058

answers:

7

Hello all, I have a language-agnostic question about an algorithm.

This comes from a (probably simple) programming challenge I read. The problem is, I'm too stupid to figure it out, and curious enough that it is bugging me.

The goal is to sort a list of integers to ascending order by swapping the positions of numbers in the list. Each time you swap two numbers, you have to add their sum to a running total. The challenge is to produce the sorted list with the smallest possible running total. Examples:

3 2 1 - 4
1 8 9 7 6 - 41
8 4 5 3 2 7 - 34

Though you are free to just give the answer if you want, if you'd rather offer a "hint" in the right direction (if such a thing is possible), I would prefer that.

Thanks!

A: 

As a hint, this reeks of dynamic programming; that might not be precise enough a hint to help, but I'd rather start with too little!

Zach Snow
A: 

You are charged by the number of swaps, not by the number of comparisons. Nor did you mention being charged for keeping other records.

John the Statistician
+1  A: 

Comparisons and traversals apparently come for free, you can pre-calculate the "distance" a number must travel (and effectively the final sort order). The puzzle is the swap algorithm.

Minimizing overall swaps is obviously important. Minimizing swaps of larger numbers is also important.

I'm pretty sure an optimal swap process cannot be guaranteed by evaluating each ordering in a stateless fashion, although you might frequently come close (not the challenge).

HUAGHAGUAH
+1  A: 

I'm assuming memory is free and you can simulate the sort before performing it on the real objects.

One approach (that is likely not the fastest) is to maintain a priority queue. Each node in the queue is keyed by the swap cost to get there and it contains the current item ordering and the sequence of steps to achieve that ordering. For example, initially it would contain a 0-cost node with the original data ordering and no steps.

Run a loop that dequeues the lowest-cost queue item, and enqueues all possible single-swap steps starting at that point. Keep running the loop until the head of the queue has a sorted list.

Mr Fooz
A: 

I think there is no trivial solution to this problem, and my approach is likely no better than the priority queue approach.

Find the smallest number, N. Any pairs of numbers that occupy each others' desired locations should be swapped, except for N. Assemble (by brute force) a collection of every set of numbers that can be mutually swapped into their desired locations, such that the cost of sorting the set amongst itself is less than the cost of swapping every element of the set with N. These sets will comprise a number of cycles. Swap within those cycles in such a way that the smallest number is swapped twice. Swap all remaining numbers, which comprise a cycle including N, using N as a placeholder.

Sparr
There is already an accepted solution which is more-or-less the correct answer -- you can *prove* that (with the obvious modification) it's optimal. "Reducing" something to an NP-hard problem is too easy; that doesn't say anything about the hardness of the original problem :P
ShreevatsaR
Why would you say "already" when I posted mine an hour before the accepted answer? Ignoring for a moment that the accepted answer is wrong.
Sparr
There is already one counter-example to your hypothesis. See Raymond's analysis.
recursive
To whose hypothesis ("your" is ambiguous in a flat comment list, sadly)? Raymond's analysis supports my answer, and my objection to the accepted solution and Shreevatsa's comment.
Sparr
+2  A: 

I did a few attempts at solving one of the examples by hand:

  • 1 8 9 7 6
  • 6 8 9 7 1 (+6+1=7)
  • 6 8 1 7 9 (7+1+9=17)
  • 6 8 7 1 9 (17+1+7=25)
  • 6 1 7 8 9 (25+1+8=34)
  • 1 6 7 8 9 (34+1+6=41)

Since you needed to displace the 1, it seems that you may have to do an exhaustive search to complete the problem - the details of which were already posted by another user. Note that you will encounter problems if the dataset is large when doing this method.

If the problem allows for "close" answers, you can simply make a greedy algorithm that puts the largest item into position - either doing so directly, or by swapping the smallest element into that slot first.

Raymond Martineau
+5  A: 

Only read the first two paragraph is you just want a hint. There is a an efficient solution to this (unless I made a mistake of course). First sort the list. Now we can write the original list as a list of products of disjoint cycles.

For example 5,3,4,2,1 has two cycles, (5,1) and (3,4,2). The cycle can be thought of as starting at 3, 4 is in 3's spot, 2 is in 4's spot, and 4 is in 3's. spot. The end goal is 1,2,3,4,5 or (1)(2)(3)(4)(5), five disjoint cycles.

If we switch two elements from different cycles, say 1 and 3 then we get: 5,1,4,2,3 and in cycle notation (1,5,3,4,2). The two cycles are joined into one cycle, this is the opposite of what we want to do.

If we switch two elements from the same cycle, say 3 and 4 then we get: 5,4,3,2,1 in cycle notation (5,1)(2,4)(3). The one cycle is split into two smaller cycles. This gets us closer to the goal of all cycles of length 1. Notice that any switch of two elements in the same cycle splits the cycle into two cycles.

If we can figure out the optimal algorithm for switching one cycle we can apply that for all cycles and get an optimal algorithm for the entire sort. One algorithm is to take the minimum element in the cycle and switch it with the the whose position it is in. So for (3,4,2) we would switch 2 with 4. This leaves us with a cycle of length 1 (the element just switched into the correct position) and a cycle of size one smaller than before. We can then apply the rule again. This algorithm switches the smallest element cycle length -1 times and every other element once.

To transform a cycle of length n into cycles of length 1 takes n - 1 operations. Each element must be operated on at least once (think about each element to be sorted, it has to be moved to its correct position). The algorithm I proposed operates on each element once, which all algorithms must do, then every other operation was done on the minimal element. No algorithm can do better.

This algorithm takes O(n log n) to sort then O(n) to mess with cycles. Solving one cycle takes O(cycle length), the total length of all cycles is n so cost of the cycle operations is O(n). The final run time is O(n log n).

theycallhimtom
You have the right idea, but you've missed one trick. (Not posting it as an answer because the original poster only wanted a hint.) Here's a hint for *you* -- see how to sort (1 8 9 7 6) with cost just 41, not 42 :-) [Hint: you have to use the 1 even though it's in the right place.]
ShreevatsaR
The 18976 case exhibits the exception covered in my solution, in that a sufficiently small 'temp' number is more efficient to use to swap other numbers individually than to use a single extra number from a cycle twice, in some cases.
Sparr