ANALYSIS of ALGORITHMS, Bulletin Board

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Quickselect and inversions.




Greetings.

The following question should be answerable by setting up a recurrence 
for the generating functions that are involved and solving the
recurrence. I have tried this myself but I haven't yet been able to
find the right recurrence.

Question. What amount of disorder is left in a permutation after one
of its elements has been selected with quickselect?

This can be rephrased as follows. Compute the "grand average" for the
expected number of inversions left in a permutation after one of its
elements has been selected with quickselect.

Here are the first few generating functions.

1
0/1
4
0/4
16 + 2z
2/18
70 + 20z + 4z^2 + 2z^3
34/96
335 + 159z + 56z^2 + 32z^3 + 10z^4 + 6z^5 + 2z^6
449/600
1767 + 1180z + 574z^2 + 381z^3 + 180z^4 + 116z^5 + 64z^6 + 30z^7 + 18z^8 + 8z^9 + 2z^10
5601/4320
10099 + 8906z + 5408z^2 + 3967z^3 + 2378z^4 + 1678z^5 + 1080z^6 + 652z^7 + 454z^8 + 292z^9 + 170z^10 + 98z^11 + 58z^12 + 28z^13 + 10z^14 + 2z^15
70837/35280

Regards,

Marko Riedel



#! /usr/bin/perl -w
#

sub fact {
  my ($n) = @_;

  return 1 if $n==0 or $n==1;

  my ($p) = (1);
  $p*=$n-- while $n>1;

  return $p;
}

sub qsel {
  my ($perm, $lower, $upper, $index) = @_;
  my ($temp);

  return if $lower==$upper;

  if($lower+1==$upper){
    $temp=$perm->[$lower];

    if($temp>$perm->[$upper]){
      $perm->[$lower]=$perm->[$upper];
      $perm->[$upper]=$temp;
    }

    return;
  }

  my ($pivot, $i, $j) = 
    ($perm->[$lower], $lower+1, $upper);
  while($i<=$j){
    while($i<=$j && $perm->[$i]<$pivot){
      $perm->[$i-1]=$perm->[$i];
      $i++;
    }
    while($j>=$i && $perm->[$j]>$pivot){
      $j--;
    }
    
    if($i<$j){
      $temp=$perm->[$i];
      $perm->[$i]=$perm->[$j];
      $perm->[$j]=$temp;
    }
  }
  $perm->[$i-1]=$pivot;

  qsel($perm, $lower, $i-2, $index) 
    if $i-2>$lower && $index<=$i-2;
  qsel($perm, $i, $upper, $index-$i) 
    if $i<$upper && $index>=$i;
}

sub nextperm {
  my ($n, $i, $j) = @_;
  my (@crep, $rem, @source, @perm, $item);
 
  for($j=1; $j<=$n; $j++){
    $rem=$i%$j;
    unshift @crep, $rem;
    $i=($i-$rem)/$j;
  }

  @source=(0..$n-1);
  for($j=0; $j<$n; $j++){
    $item=splice @source, $crep[$j], 1;
    push @perm, $item;
  }

  return \@perm;
}

sub inversions {
  my ($perm) = @_;
  my ($n, $count, $i, $j) = (scalar @$perm, 0);

  for($i=0; $i<$n; $i++){
    for($j=$i+1; $j<$n; $j++){
      $count++ if $perm->[$i]>$perm->[$j];
    }
  }

  return $count;
}

sub gf2string {
  my ($poly, @str, $term) = @_;

  foreach my $i ($poly->[0] .. $poly->[1]){
    if(defined $poly->[2][$i]){
      $term=($i == 0 ? '' : ($i == 1 ? 'z' : "z^$i"));
      push @str, 
      ($poly->[2][$i] == 1 ? 
       ($i==0 ? '1' : '') : "$poly->[2][$i]") .	$term;
    } 
  }

  return join ' + ', @str;
}

sub gfat1 {
  my ($poly, $total, $total1) = (@_, 0, 0);

  foreach my $i ($poly->[0] .. $poly->[1]){
    if(defined $poly->[2][$i]){
      $total  += $poly->[2][$i];
      $total1 += $i * $poly->[2][$i];
    }
  }

  return [$total1, $total];
}



MAIN: {
  my ($max, $n) = @ARGV;

  die "$0 <n>\n" if not defined $max;

  for($n=1; $n<=$max; $n++){
    my ($gf, $i, $j, $perm, $pcopy, $invs) =
      ([undef, undef, []]);
    for($i=0; $i<fact($n); $i++){
      $perm=nextperm($n, $i);
      
      for($j=0; $j<$n; $j++){
	$pcopy=[@$perm];
	qsel($pcopy, 0, $n-1, $j);
	$invs=inversions($pcopy);
      
	$gf->[0]=$invs 
	  if not defined $gf->[0] or $gf->[0]>$invs;
	$gf->[1]=$invs 
	  if not defined $gf->[1] or $gf->[1]<$invs;
	$gf->[2][$invs]=0 
	  if not defined $gf->[2][$invs];
	$gf->[2][$invs]++;
      }
    }
    
    print gf2string($gf) . "\n";
    my ($at1) = gfat1($gf);
    print "$at1->[0]/$at1->[1]\n";
  }
}

Date Prev | Date Next | Date Index | Thread Index