Toggle navigation OptaPlanner logo
  • Home
  • Download
  • Learn
    • Documentation
    • Videos
    • Slides
    • Training
    • Use cases
    • Compatibility
    • Testimonials and case studies
  • Get help
  • Source
  • Team
  • Services
  • Star
  • @OptaPlanner
  • Fb
Fork me on GitHub
  • Exploring the new OptaWeb Employee Rostering ba...
  • Constraint Streams - Modern Java constraints wi...

How to plan (and optimize) a Secret Santa

Wed 18 December 2019

Avatar Christopher Chianelli

Christopher Chianelli


GitHub

OptaWeb Employee Rostering developer

Many workplaces host a "Secret Santa", where each employee gets assigned a coworker whom they need to buy a present for. This fosters good relations between the employees and brings them closer together. But what about global companies, where coworkers may be many miles apart? Ideally, we want employees who are further apart to give gifts to each other, since they are the ones who probably know the least about each other. Let’s optimize it with OptaPlanner!

The Constraints

One of the most obvious constraints is that everyone should get a gift. Or phrased a little differently: no one should get multiple gifts.

Here is how one would create this constraint in OptaPlanner using the constraint streams API:

private Constraint sameReceiverConflict(ConstraintFactory constraintFactory) {
    return constraintFactory
            .fromUniquePair(SecretSantaAssignment.class,
                    Joiners.equal(SecretSantaAssignment::getReceiver))
            .penalize("Same Receiver", HardMediumSoftBigDecimalScore.ONE_HARD);
}

Next, we need to reward assignments where the gifter and the receiver are farther apart. Here’s how we do it with constraint streams:

private Constraint largerDistanceAward(ConstraintFactory constraintFactory) {
    return constraintFactory
            .from(SecretSantaAssignment.class)
            .rewardConfigurableBigDecimal("secretFactor", "Larger Distance Award",
                              (m) -> BigDecimal.valueOf(Location.calculateDistanceBetween(m.getGifter().getLocation(),
                                                                       m.getReceiver().getLocation())));
}

Let’s try solving with just these two constraints to see what we get:

noPairChecking

Alice is assigned to Daniella, Daniella is assigned to Alice…​ Bob is assigned to Austin, Austin is assigned to Bob…​ Something about this solution seems odd; it only has assignments where the gifter is the receiver’s recipient!

What happened?

This is not odd at all, but rather a consequence of how we defined our constraints: if Alice is the person furthest from Daniella, then Daniella is most likely the person furthest from Alice. This means our "Larger Distance Award" constraint will strongly favor pairs of employees.

Is this a bad thing? Yes, it is: imagine if Alice got a $100 gift from Daniella, but only got Daniella a $10 gift. Alice will probably feel bad, and Daniella will feel disappointed. If Alice gave the $10 gift to Bob and Bob gave a $30 gift to Daniella, Alice wouldn’t feel bad and Daniella wouldn’t feel cheated.

Let’s add this constraint (using, you guessed it, constraint streams!)

private Constraint giftPair(ConstraintFactory constraintFactory) {
    return constraintFactory
            .fromUniquePair(SecretSantaAssignment.class,
            // Here, we are joining (a,b) where:
            // a.gifter = b.receiver
            // and
            // a.receiver = b.gifter
            // In other words: a's gifter is getting a gift from b's gifter
            // and b's gifter is getting a gift from a's gifter, which mean
            // we have a pair!
                            Joiners.equal(SecretSantaAssignment::getGifter, SecretSantaAssignment::getReceiver),
                            Joiners.equal(SecretSantaAssignment::getReciever, SecretSantaAssignment::getGifter))
            .penalize("Gifter-Receiver Cycle", HardMediumSoftBigDecimalScore.ONE_MEDIUM);
}

This constraint should be a medium constraint - we want to avoid it if possible, but it isn’t as much of a deal breaker as someone not receiving a gift.

Now let’s try solving again after adding the gift pair constraint:

withPairChecking

Much better; we end up with two chains: Alice gives to Austin who gives to Bob who gives to Charlie who gives to Alice; and Dina gives to Dennis who gives to Daniella who gives to Julian who gives to Dina.

This is just the beginning of what you can do to optimize Secret Santa using OptaPlanner. For instance, you can do the following:

  • Allow each person to input a list of people who they do not want to be Secret Santa for, and add a medium constraint that ensures they are not the Secret Santa for anyone in their list (if possible).

  • Use a secret factor that sightly influences distance so people cannot find out who their Secret Santa is just by running OptaPlanner.

  • Pin a Secret Santa assignment to force OptaPlanner to use that assignment in its solution.

You can learn more about OptaPlanner by visting the OptaPlanner website and find the full Secret Santa OptaPlanner example on my GitHub.


Comments Permalink
 tagged as use case

Comments

Visit our forum to comment
  • Exploring the new OptaWeb Employee Rostering ba...
  • Constraint Streams - Modern Java constraints wi...
Atom News feed
Don't want to miss a single blog post?
Follow us on
  • T
  • Fb
Blog archive
Latest release
  • 8.2.0.Final released
    Tue 9 February 2021
Upcoming events
  • Javaland
    Worldwide - Tue 16 March 2021
    • AI on Quarkus: I love it when an OptaPlan comes together by Geoffrey De Smet
  • SouJava MOTU
    Worldwide - Thu 15 April 2021
    • Planejamento de Recursos com OptaPlanner by Karina Varela, Otávio Santana
Add event / Archive
Latest blog posts
  • How much faster is Java 15?
    Tue 26 January 2021
     Michal Tomčo
  • Solve the facility location problem
    Fri 9 October 2020
     Jiří Locker
  • OptaPlanner Week 2020 recordings
    Mon 7 September 2020
     Geoffrey De Smet
  • Let’s OptaPlan your jBPM tasks (part 1) - Integrating the two worlds
    Fri 3 July 2020
     Walter Medvedeo
  • AI versus Covid-19: How Java helps nurses and doctors in this fight
    Fri 8 May 2020
     Christopher Chianelli
  • Workflow processes with AI scheduling
    Tue 5 May 2020
     Christopher Chianelli
  • Constraint Streams - Modern Java constraints without the Drools Rule Language
    Tue 7 April 2020
     Geoffrey De Smet
Blog archive
Latest videos
  • YT Maintenance scheduling
    Wed 24 February 2021
     Julian Cui
  • YT Vaccination appointment scheduling
    Wed 3 February 2021
     Geoffrey De Smet
  • YT Shadow variables
    Tue 19 January 2021
     Geoffrey De Smet
  • YT Domain modeling and design patterns
    Tue 17 November 2020
     Geoffrey De Smet
  • YT Quarkus insights: AI constraint solving
    Tue 20 October 2020
     Geoffrey De Smet
  • YT AI in kotlin
    Wed 23 September 2020
     Geoffrey De Smet
  • YT Planning agility: continuous planning, real-time planning and more
    Thu 3 September 2020
     Geoffrey De Smet
Video archive

KIE projects

  • Drools rule engine
  • OptaPlanner constraint solver
  • jBPM workflow engine

Community

  • Blog
  • Get Help
  • Team
  • Governance
  • Academic research

Code

  • Build from source
  • Submit a bug
  • License (Apache-2.0)
  • Release notes
  • Upgrade recipes
Sponsored by
Red Hat
More coder content at
Red Hat Developers
© Copyright 2006-2021, Red Hat, Inc. or third-party contributors - Privacy statement - Terms of use - Website info