My solution first divides people into their families.  I build up a list 
of santas (where each person gives to the next in the list, and the tail 
gives to the head) by:

1) For the first santa, choose from the family with the most people.
2) For all other santas, choose from the family with the most remaining 
people that is not the family of the last santa.

I wasn't able to think of any situations that would thwart this strategy.

I, too, just print out the names at the console instead of e-mailing. 
And  there is also nothing random about my strategy, so given the same 
input you will always get the same output.  It would be a simple matter 
to shuffle the members within the families though; that would mix things 
up a little.

Note to other submitters, the following is a good edge case to test:

Luke1 Skywalker <luke / theforce.net>
Luke2 Skywalker <luke / theforce.net>
Luke3 Skywalker <luke / theforce.net>
Luke4 Skywalker <luke / theforce.net>
Leia Skywalker <leia / therebellion.org>
Toula Portokalos <toula / manhunter.org>
Gus Portokalos <gus / weareallfruit.net>
Bruce Wayne <bruce / imbatman.com>
Virgil Brigman <virgil / rigworkersunion.org>

---

# Represents a family.
class Family
	attr_reader :name, :members

	def initialize(name)
		@name = name
		@members = []
	end
	
	# Number of people in family.
	def count
		@members.length
	end
	
	# Pop the last member off.
	def pop
		@members.pop
	end

	# Compare by size.
	def <=>(other)
		count <=> other.count
	end
end

class Person
	attr_reader :first_name, :last_name, :email
	
	def initialize(first_name, last_name, email)
		@first_name = first_name
		@last_name = last_name
		@email = email
	end
	
	def to_s
		"#{@first_name} #{@last_name} <#{@email}>"
	end
end

familyTable = Hash.new {|h,k| h[k] = Family.new(k)}

while line = gets
	line =~ /(\w+) (\w+) <(.+)>/
	first, last, email = $1, $2, $3
	
	familyTable[last].members << Person.new(first, last, email)
end

puts
puts "Processing..."

families = familyTable.values
santas = []

while families.length > 0

	families.sort!.reverse!

	if families.first.count == 0
		# nobody is left; we're done
		break
	end
	
	if santas.length == 0
		# for the very first santa, choose someone from
		# the largest family
		santas << families.first.pop
	else
		success = false
		
		# find largest family that is not one's own
		families.each do |family|
			if family.name != santas.last.last_name
				santas << family.pop
				success = santas.last
				break
			end
		end
		
		raise "No solution." unless success
	end
end

puts "Success!"
puts

lastSanta = santas.last
santas.each do |santa|
	puts santa.to_s + " => " + lastSanta.to_s
	lastSanta = santa
end