Probability: Secret Santa with Two donors / Person this is my first question here. I hope you can help me.
I want to do Secret Santa with my family with following modifications:


*

*For each person there are 2 donors.

*Only the 2 donors know from each other (so they can buy a gift together, too)


Practically it would go like this, 7 persons:

There are 7 cards with the names of the 7 persons.
First Round: Everybody draws a card, if it's the own name all have to draw again.
If it's a success the person who drew the name will be the persons first donor. He will write his name hidden inside the card - so that the second donor in the next round will know the other donor.
After this round every person has one donor.
Second Round: Again, everybody draws a card, but this time it can't be the own name NOR the person he already is donor for. Otherwise redo.
If it's a success everybody can keep the actual card. He is now the second donor and knows the first donor for this person.

I hope the situation is clear.
I wondered if I can calculate how many tries the group needs, especially for the second round / respectively what the probabilty is to get a working result.
I wrote an algorithm which gives me for the first round a probabilty of 1854/7!=0,37 and for the second round 1.073.760/(7!7!)=0,04.
I'm not sure if I've done a mistake.
The result would mean that the second round would be pretty horrible. 0,04, that means 25 repeats in average.
If I'm right, I'm also thankful for some ideas to fasten things up a bit.

Here's the code. I'm grateful for checking it. It's written in C#. Its form needs a richtextbox, a label and a button.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace SuperSecretDoubleSanta
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }


        private int resultA = 0;
        private int resultB = 0;
        private int possibilitiesA = 0;

        private void button1_Click(object sender, EventArgs e)
        {
            int i = int.Parse(textBox1.Text);
            richTextBox1.Text = "";
            //Calculate the probabilty for 1 successful drawing
            richTextBox1.Text += calc(i, false) + "\r\n";
            possibilitiesA = resultA;

            //Calculate the probabilty for 2 sucessful drawings
            richTextBox1.Text += calc(i, true);
        }


        //startpoint for calculating the probabilty for 1 or 2 drawings
        private string calc(int numberOfPersons, bool drawtwotimes)
        {
            resultA = 0;//sucessful drawings in drawing 1
            resultB = 0;//sucessful drwaings in drawing 2

            draw(1, listAllPersons(numberOfPersons), new List<int>(),null, drawtwotimes,false);

            //Output for only 1 drawing
            if (!drawtwotimes) return "Drawing 1:\r\nProbabilty (for success): "+ resultA + "/" + numberOfPersons + "! = " + Math.Round((double)resultA / (double)factorial(numberOfPersons),2) + "\r\n";

            //Output for 2 drawings
            return "Drawing 2:\r\nProbabilty (for success): " + resultB + "/(" + numberOfPersons + "!"+numberOfPersons+"!) = " + Math.Round((double)resultB / ((double)factorial(numberOfPersons) * factorial(numberOfPersons)),2) + "\r\n";
        }


        //Main-Function

        public void draw(int drawingPerson, List<int> remainingPersons, List<int> drawnPersons, List<int> drawnPersonsB, bool drawtwotimes, bool isSecondDraw)
        {
            if (remainingPersons.Count == 1)//only 1 person left: gives result
            {
                if (remainingPersons[0] != drawingPerson)//The drawing person isn't allowed to draw himself.
                {
                    if (drawtwotimes)
                    {
                        if (!isSecondDraw)//do 2 Drawings. It's a successful Drawing 1.
                        {
                            drawnPersons.Add(remainingPersons[0]);//add the last drawn person

                            //now do Drawing 2
                            draw(1, listAllPersons(drawnPersons.Count), drawnPersons, new List<int>(), drawtwotimes, true);

                            //show progress
                            resultA++;//successful Drawing 1
                            label1.Text = resultA + " / " + possibilitiesA;
                            Application.DoEvents();
                        }
                        else if (remainingPersons[0] != drawnPersons.Last())
                            //the drawing person isn't allowed to draw the same person as in Drawing 1
                        {
                            //successful result
                            drawnPersonsB.Add(remainingPersons[0]);//could be printed...
                            resultB++;
                        }
                    }
                    else
                    {
                        resultA++;//only do Drawing 1. It's a successful draw
                    }

                }
                return;//last person has drawn
            }

            //traverse all remaining drawable persons
            foreach (int drawnPerson in remainingPersons)
            {
                if (drawnPerson == drawingPerson) continue;//person isn't allowed to draw himself
                if (drawtwotimes && isSecondDraw && drawnPerson == drawnPersons[drawingPerson-1]) 
                    continue;//person isn't allowed to draw the person from drawing 1 again.

                List<int> newdrawnPersonsA = new List<int>(drawnPersons);//list of drawn persons in Drawing 1
                List<int> newdrawnPersonsB;//list of drawn persons in Drawing 2
                if (!isSecondDraw)
                {
                    newdrawnPersonsA.Add(drawnPerson);//adds a drawn person in Drawing 1
                    newdrawnPersonsB=null;//not needed in Drawing 1

                }
                else {
                    newdrawnPersonsB = new List<int>(drawnPersonsB);
                    newdrawnPersonsB.Add(drawnPerson);//adds a drawn person in Drawing 2
                }
                List<int> newremainingPersons = new List<int>(remainingPersons);
                newremainingPersons.Remove(drawnPerson);//the next person can draw one person less: the actual drawn person

                //next person draws...
                draw(drawingPerson + 1, newremainingPersons, newdrawnPersonsA, newdrawnPersonsB, drawtwotimes, isSecondDraw);
            }
        }


        //generates a list with all persons
        public static List<int> listAllPersons(int numberOfPersons)
        {
            List<int> t = new List<int>(numberOfPersons);
            for (int i = 1; i <= numberOfPersons; i++) t.Add(i);
            return t;
        }


        //calculates 7! etc.
        public static int factorial(int c)
        {
            int e = 1;
            for (int i = c; i > 1; i--)
            {
                e *= i;
            }
            return e;
        }

    }
}

 A: What if you'd take 7 colors. Each person takes 2 notes of 1 color (secretly) and writes his name on the note and remembers his own color without telling.
When drawing, take 2 differently colored notes (not your own color).
You still my have some false drawings (e.g. for last person 2 notes left of same color or last person's color), but probably only a small amount.
Contextual information from drawings could be present, but probably not suitable for non-mathemagicians.
Hmm. does not work yet, to determine co-donor, after success, you need to write the co-donor on the notes, draw again, to keep this a secret.
A: I'm afraid it is time to learn python, which has very powerful libraries for this kind of problems:
import itertools
import random

def derangements(n):
    'All deranged permutations of the integers 0..n-1 inclusive'
    return ( perm for perm in itertools.permutations(range(n))
             if all(indx != p for indx, p in enumerate(perm)) )


def different(d1, d2):
    'All elements of d1 and d2 are different req.(len(d1) == len(d2))'
    return (all(d2[indx] != p for indx, p in enumerate(d1)))


l7 = list(derangements(7))

success = 0
tried = 0
successes = []

for i1 in l7:
    for i2 in l7:
        tried = tried + 1
        if (different(i1,i2)):
            success = success + 1
            successes.append((i1,i2))


float(success) / (5040*5040)

print(random.choice(successes))

Which resulted in: percentage and sample:
0.0422713529856387
((4, 5, 6, 0, 1, 3, 2), (2, 0, 1, 5, 3, 6, 4))

Where we have the percentage and a sample with two derangements that have no element in common.
