On 21 Apr 2003 at 1:10, Mauricio FernŠŌdez wrote about transition
matrices used for Hal's problem of "biases weighted random" 
sequences.

> (nice maths snipped)

How about the following approach, though mathematically not as 
interesting as transition matrices:

Given a set of values Vi and their desired number of occurrences in 
the final sequence Ni, it can be shown that there exists a sequence 
as Hal described if and only if

  max( Ni ) <= ( sum( Ni ) + 1 ) / 2

If anybody is interested in the proof, I can try to translate it to 
English and send it in another post.

Using this simple test you can implement the following algorithm 
based on the weighted random algorithm shown in other posts:


  def hal_sequence( values )
  
    sequence = []
    last_value = nil
    last_count = 0
    size = values.values.inject( 0 ) { | count, sum | sum + count }
    
    size.times do
      
      next_value = nil
      max = ( size + 1 ) / 2                               # (3)
      index = rand( size - last_count )                    # (1)
      
      values.each do
        | value, count |
        raise "no possible hal sequence" if count > max    # (3)
        if count == max && 2 * max > size                  # (4)
          next_value = value
          break
        end
        next if value == last_value                        # (1)
        next_value ||= value if ( index -= count ) < 0     # (5)
      end
      
      sequence << next_value
      last_value = next_value
      last_count = values[ last_value ] -= 1               # (2)
      values.delete( last_value ) if last_count <= 0       # (2)
      size -= 1
      
    end
  
    sequence
    
  end


Here are some examples:

  puts hal_sequence( 'a' => 3, 'b' => 4, 'c' => 3 ).join
  # => cbabcababc
  puts hal_sequence( 'a' => 3, 'b' => 6, 'c' => 3 ).join
  # => abcbabcbabcb
  puts hal_sequence( 'a' => 3, 'b' => 8, 'c' => 3 ).join
  # => ./hal.rb:17:in `hal_sequence': no possible hal sequence


Some notes about the algorithm:

(1) The last value taken and its count (remaining number of 
occurrences) is ignored when choosing the next random index and the 
next value.

(2) The remaining values Vi and their numbers of occurrences Ni are 
adjusted after each pass.

(3) The test for an existing "hal sequence" could be extracted out of 
the loop, but I like this code better.

(4) For each pass, if there's a value Vi with

  Ni == ( sum( Ni ) + 1 ) / 2

and sum( Ni ) is odd, then the next element in the sequence MUST be 
Vi (as well as every second remaining element).

(5) Because of (4), the loop cannot be exited after picking the next 
value based on the random index, for we have to test each value for 
the condition (4).


Regards,
Pit