in reply to Puzzle: The Ham Cheese Sandwich cut.

The warm-up problem isn't so trivial. Two possible solutions are decribed in Cormen – Leiserson – Rivest – Stein: Introduction to Algorithms. One of these is randomized and runs in expected O(n) time. (Update: the other one runs in guaranteed O(n) time, as Perl Mouse has noted in his reply. I'm sorry this wasn't clear from my original post.)

I've recently implemented this randomized algorithm for perl, although my implementation is not a very efficent one, as it would be possible to do all its operations in place (with only O(n) extra memory and more importantly less time).

The rest of this post shows my implementation.

Here's the code although it might not make much sense without context. The quantile function is passed a positive ordinal $r and a list of numbers @a. It returns the $r-th least number from the list if it would be sorted, with $r = 1 meaning to return the minimum of the list. If you want a median, pass $r = int((@a + 1)/2)

sub quantile { my($n, @a) = @_; my($q); @a < $n and return 9e9999; $q = quantile1($n, @a); if (1) { # DEBUG check postcondition my($nl, $nle); $nl = grep { $_ < $q } @a; $nle = grep { $_ <= $q } @a; $nl < $n && $n <= $nle or die "postcondition failed"; } $q; } sub quantile1 { my($n, @a) = @_; my($c, $v, @l, @h); @a <= 1 and do { @a or die "internal error: quantile1 called on empty + list, n=" . $n; return $a[0]; }; $c = @a[rand(@a)]; for $v (@a) { if ($v < $c) { push(@l, $v); } elsif ($c < $v) { push(@h, $v); } } if ($n <= @l) { quantile1($n, @l); } elsif ($n > @a - @h) { quantile1($n - @a + @h, @h); } else { $c; } }

Replies are listed 'Best First'.
Re^2: Puzzle: The Ham Cheese Sandwich cut.
by Perl Mouse (Chaplain) on Nov 18, 2005 at 10:12 UTC
    One of these is randomized and runs in expected O(n) time.

    The rest of this post shows my implementation.

    Nice, but the worst case running time is Ω(n2). It suffers from the same problem as Quicksort: picking a random pivot works well often enough to get a good expected running time, but if you're unlucky, it's really slow.

    There is an algorithm to do it in garanteed linear time (although when done in Perl, the constants are so high that for most practical situations, one can better use sorting in C and picking the middle element).

    Perl --((8:>*