{"id":"randomness-drawing","slug":"drawing-and-seeds","title":"Drawing without repeats, and seeds","text":"A teacher picks three students to present their projects today, and nobody should present twice. Picking with `choice` three times looks like it should work, but nothing stops it from picking the same student again:\n\n```python\nimport random\n\nrandom.seed(3)\nstudents = [\"Ada\", \"Ben\", \"Cy\", \"Di\", \"Eve\", \"Finn\"]\nfor i in range(3):\n    print(random.choice(students))\n```\n\n```output\nBen\nEve\nEve\n```\n\nEve has to present twice. Every call of `choice` is independent: it doesn't know which students it picked before, so each pick can land on anyone. The same goes for `randint`.\n\n### sample\n\nWhat the teacher needs is like drawing names out of a hat: a name that has been drawn isn't in the hat anymore. That's what `random.sample(items, count)` does. It picks `count` different items of `items` and returns them in a new list:\n\n```python\nimport random\n\nrandom.seed(7)\nstudents = [\"Ada\", \"Ben\", \"Cy\", \"Di\", \"Eve\", \"Finn\"]\npresenters = random.sample(students, 3)\nprint(\"Presenting today:\", presenters)\nprint(\"The class:\", students)\n```\n\n```output\nPresenting today: ['Cy', 'Ben', 'Di']\nThe class: ['Ada', 'Ben', 'Cy', 'Di', 'Eve', 'Finn']\n```\n\nUnlike `shuffle`, `sample` leaves the list it was given as it was. It never picks the same item of the list twice, so if the list has no repeated values, neither does the sample.\n\nThe items can be a range too. A puzzle game hides 4 mines on a board whose squares are numbered 1 to 25, and two mines can't share a square:\n\n```python\nimport random\n\nrandom.seed(7)\nmines = random.sample(range(1, 26), 4)\nprint(\"Mines on squares:\", sorted(mines))\n```\n\n```output\nMines on squares: [5, 11, 13, 21]\n```\n\nThe sample comes in the order it was drawn; `sorted` only makes it easier to read. There must be enough items to pick from. Asking for 5 different students out of 3 is an error, and the error message calls the items the **population**:\n\n```python\nimport random\n\nstudents = [\"Ada\", \"Ben\", \"Cy\"]\nprint(random.sample(students, 5))\n```\n\n```crash\nTraceback (most recent call last):\n  File \"main.py\", line 4, in <module>\n    print(random.sample(students, 5))\n          ~~~~~~~~~~~~~^^^^^^^^^^^^^\nValueError: Sample larger than population or is negative\n```\n\n### Where random numbers come from\n\nA computer follows its instructions exactly, so it can't really roll a die. Instead, `random` calculates its numbers with a formula: each new number comes from the state the previous one left behind. The value the whole series starts from is called the **seed**. The same seed always gives the same series of numbers:\n\n```pycon\n>>> import random\n>>> random.seed(42)\n>>> random.randint(1, 100)\n82\n>>> random.randint(1, 100)\n15\n>>> random.seed(42)\n>>> random.randint(1, 100)\n82\n```\n\nWhen a program doesn't set a seed, Python picks one from something that changes all the time, which is why every run is different. Numbers made like this are called **pseudorandom**: they look random and are good enough for games and simulations, but whoever knows the seed can repeat them. (For passwords and other secrets, Python has the `secrets` module.)\n\nA fixed seed is useful in its own right. A program that misbehaves only now and then can be made to misbehave on every run, so you can find the bug. The checks on this site use seeds the same way to test functions that use random numbers.\n","task":"What does this program print? Type each line it prints.\n","kind":"predict","seed":"import random\n\nrandom.seed(10)\nfirst = random.randint(1, 100)\nrandom.seed(10)\nsecond = random.randint(1, 100)\nprint(first == second)\nnumbers = random.sample(range(1, 6), 5)\nprint(sorted(numbers))\n","solution":"import random\n\nrandom.seed(10)\nfirst = random.randint(1, 100)\nrandom.seed(10)\nsecond = random.randint(1, 100)\nprint(first == second)\nnumbers = random.sample(range(1, 6), 5)\nprint(sorted(numbers))\n","hints":["Setting the same seed again starts the same series of numbers again.","`range(1, 6)` is the numbers 1 to 5, and the sample takes all five of them, in some order.","`sorted` puts them in order, so the order the sample picked them in doesn't matter."],"tests":[{"name":"Output","stdin":"","expect":"True\n[1, 2, 3, 4, 5]","hidden":false}],"require":[],"forbid":[],"lines":["True","[1, 2, 3, 4, 5]"],"more":[{"task":"A quiz app asks a few questions from its list in each round, and never the same one twice in a round. Write a function `pick_questions(questions, count)` that returns a list of `count` different questions picked at random from the list `questions` with `random.sample`. When `count` is larger than the number of questions, it returns all the questions, in a random order. The list `questions` must stay as it was.\n\nThe checks set the seed themselves before they call the function.\n","kind":"code","seed":"import random\n\n\ndef pick_questions(questions, count):\n    pass\n\n\nif __name__ == \"__main__\":\n    questions = [\"What is 7 * 8?\", \"Who wrote Hamlet?\", \"What color is a ruby?\"]\n    print(pick_questions(questions, 2))\n    print(pick_questions(questions, 10))\n","solution":"import random\n\n\ndef pick_questions(questions, count):\n    if count > len(questions):\n        count = len(questions)\n    return random.sample(questions, count)\n\n\nif __name__ == \"__main__\":\n    questions = [\"What is 7 * 8?\", \"Who wrote Hamlet?\", \"What color is a ruby?\"]\n    print(pick_questions(questions, 2))\n    print(pick_questions(questions, 10))\n","hints":["`random.sample(questions, count)` picks `count` different questions and returns them in a new list, without changing `questions`.","`sample` can't pick more items than there are. Before sampling, check whether `count` is larger than `len(questions)`, and if it is, set `count` to `len(questions)`."],"tests":[{"name":"Test 1","stdin":"","call":"import random\nrandom.seed(3)\nquiz = [\n    \"What is 7 * 8?\",\n    \"Who wrote Hamlet?\",\n    \"What color is a ruby?\",\n    \"How many legs does a spider have?\",\n    \"Which planet is the largest?\",\n]\nfor question in pick_questions(quiz, 3):\n    print(question)\n","expect":"Who wrote Hamlet?\nWhich planet is the largest?\nHow many legs does a spider have?","hidden":false},{"name":"Test 2","stdin":"","call":"import random\nrandom.seed(11)\nquiz = [\"2 + 3?\", \"4 * 5?\", \"9 - 7?\", \"8 / 2?\", \"3 ** 2?\", \"10 // 3?\"]\nprint(pick_questions(quiz, 2))\n","expect":"['8 / 2?', '3 ** 2?']","hidden":true},{"name":"Test 3","stdin":"","call":"import random\nrandom.seed(5)\nprint(pick_questions([\"Q1\", \"Q2\", \"Q3\"], 5))\n","expect":"['Q3', 'Q2', 'Q1']","hidden":true},{"name":"Test 4","stdin":"","call":"quiz = [\"Q1\", \"Q2\", \"Q3\", \"Q4\"]\npicked = pick_questions(quiz, 2)\nprint(len(picked), len(set(picked)))\nprint(quiz)\n","expect":"2 2\n['Q1', 'Q2', 'Q3', 'Q4']","hidden":true},{"name":"pick_questions([\"Q1\", \"Q2\"], 0)","stdin":"","call":"pick_questions([\"Q1\", \"Q2\"], 0)\n","expect":"[]","hidden":true,"returns":true}],"require":[{"pattern":"\\bsample\\s*\\(","message":"Pick the questions with `random.sample`."}],"forbid":[]},{"task":"Write a program that splits players into two random teams. It first asks for a seed and passes it to `random.seed`, then reads player names until an empty line. It shuffles the names with `random.shuffle` and prints the first half as team 1 and the rest as team 2. With an odd number of players, team 2 gets the extra player (`len(names) // 2` names go to team 1). For example:\n\n```\nSeed: 3\nName: Ada\nName: Ben\nName: Cy\nName: Di\nName: Eve\nName:\nTeam 1:\nAda\nCy\nTeam 2:\nDi\nEve\nBen\n```\n","kind":"code","seed":"import random\n\nrandom.seed(int(input(\"Seed: \")))\n","solution":"import random\n\nrandom.seed(int(input(\"Seed: \")))\nnames = []\nwhile True:\n    name = input(\"Name: \")\n    if name == \"\":\n        break\n    names.append(name)\n\nrandom.shuffle(names)\nhalf = len(names) // 2\nprint(\"Team 1:\")\nfor name in names[:half]:\n    print(name)\nprint(\"Team 2:\")\nfor name in names[half:]:\n    print(name)\n","hints":["Read names in a `while True` loop and append them to a list; leave the loop with `break` when the name is empty.","Shuffle the list once, after all the names are read, on a line of its own.","With `half = len(names) // 2`, team 1 is the slice `names[:half]` and team 2 is `names[half:]`. Print each name with a `for` loop."],"tests":[{"name":"Test 1","stdin":"3\nAda\nBen\nCy\nDi\nEve\n\n","expect":"Seed: 3\nName: Ada\nName: Ben\nName: Cy\nName: Di\nName: Eve\nName:\nTeam 1:\nAda\nCy\nTeam 2:\nDi\nEve\nBen","hidden":false},{"name":"Test 2","stdin":"10\nNoor\nOtto\nPia\nQuinn\n\n","expect":"Seed: 10\nName: Noor\nName: Otto\nName: Pia\nName: Quinn\nName:\nTeam 1:\nQuinn\nPia\nTeam 2:\nOtto\nNoor","hidden":true},{"name":"Test 3","stdin":"77\nRay\nSam\n\n","expect":"Seed: 77\nName: Ray\nName: Sam\nName:\nTeam 1:\nRay\nTeam 2:\nSam","hidden":true}],"require":[],"forbid":[]}]}