{"id":20139397,"url":"https://github.com/mackysoft/choice","last_synced_at":"2025-08-19T20:18:00.330Z","repository":{"id":47538920,"uuid":"330675720","full_name":"mackysoft/Choice","owner":"mackysoft","description":"Weighted random selector for Unity.","archived":false,"fork":false,"pushed_at":"2022-10-27T16:27:12.000Z","size":600,"stargazers_count":96,"open_issues_count":0,"forks_count":4,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-25T06:38:26.257Z","etag":null,"topics":["alias-method","binary-search","csharp","fast","linear-search","picker","random","selector","unity","weighted","weights"],"latest_commit_sha":null,"homepage":"https://mackysoft.github.io/Choice/","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mackysoft.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-01-18T13:31:34.000Z","updated_at":"2025-01-21T01:23:15.000Z","dependencies_parsed_at":"2023-01-20T08:26:17.357Z","dependency_job_id":null,"html_url":"https://github.com/mackysoft/Choice","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/mackysoft/Choice","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mackysoft%2FChoice","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mackysoft%2FChoice/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mackysoft%2FChoice/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mackysoft%2FChoice/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mackysoft","download_url":"https://codeload.github.com/mackysoft/Choice/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mackysoft%2FChoice/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271215037,"owners_count":24720098,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-19T02:00:09.176Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["alias-method","binary-search","csharp","fast","linear-search","picker","random","selector","unity","weighted","weights"],"created_at":"2024-11-13T21:45:07.807Z","updated_at":"2025-08-19T20:18:00.305Z","avatar_url":"https://github.com/mackysoft.png","language":"C#","readme":"﻿# Choice - Weighted Random Selector\n\n**Created by Hiroya Aramaki ([Makihiro](https://twitter.com/makihiro_dev))**\n\n[![Tests](https://github.com/mackysoft/Choice/actions/workflows/tests.yaml/badge.svg)](https://github.com/mackysoft/Choice/actions/workflows/tests.yaml) [![Build](https://github.com/mackysoft/Choice/actions/workflows/build.yaml/badge.svg)](https://github.com/mackysoft/Choice/actions/workflows/build.yaml) [![Release](https://img.shields.io/github/v/release/mackysoft/Choice)](https://github.com/mackysoft/Choice/releases) [![openupm](https://img.shields.io/npm/v/com.mackysoft.choice?label=openupm\u0026registry_uri=https://package.openupm.com)](https://openupm.com/packages/com.mackysoft.choice/)\n\n## What is Weighted Random Selector ?\n\nWeighted Random Selector is an algorithm for randomly selecting elements based on their weights.\n\nFor example.\n- Drop items based on rarity.\n- Events that occur with a certain probability\n\nIt can be used to determine things with probability.\n\nChoice is a library that was created to make it easier to implement.\n\n```cs\n// This is the simplest usage.\nvar randomSelectedItem = items\n\t.ToWeightedSelector(item =\u003e item.weight)\n\t.SelectItemWithUnityRandom();\n```\n\nGreat introduction article on Weighted Random Select: https://blog.bruce-hill.com/a-faster-weighted-random-choice\n\n\n## \u003ca id=\"index\" href=\"#index\"\u003e Table of Contents \u003c/a\u003e\n\n- [📥 Installation](#installation)\n- [🔰 Usage](#usage)\n  - [ToWeightedSelector Overloads](#toweightedselector-overloads)\n    - [from weighted entry pattern](#from-weighted-entry)\n    - [from weighted item pattern](#from-weighted-item)\n    - [from Dictionary\u003cItem,float\u003e pattern](#from-dictionary)\n  - [LINQ](#linq)\n  - [Algorithms](#algorithms)\n    - [Linear Scan](#linear-scan)\n    - [Binary Search](#binary-search)\n    - [Alias Method](#alias-method)\n    - [Speed Measurement](#speed-measurement)\n- [📔 Author Info](#author-info)\n- [📜 License](#license)\n\n\n# \u003ca id=\"installation\" href=\"#installation\"\u003e 📥 Installation \u003c/a\u003e\n\nDownload any version from releases.\n\nReleases: https://github.com/mackysoft/Choice/releases\n\n### Install via git URL\n\nOr, you can add this package by opening PackageManager and entering\n\n`https://github.com/mackysoft/Choice.git?path=Assets/MackySoft/MackySoft.Choice`\n\nfrom the `Add package from git URL` option.\n\n\n### Install via Open UPM\n\nOr, you can install this package from the [Open UPM](https://openupm.com/packages/com.mackysoft.choice/) registry.\n\nMore details [here](https://openupm.com/).\n\n```\nopenupm add com.mackysoft.choice\n```\n\n# \u003ca id=\"usage\" href=\"#requirements\"\u003e 🔰 Usage \u003c/a\u003e\n\n```cs\n// To use Choice, add this namespace.\nusing MackySoft.Choice;\n\npublic class WeightedItem {\n\tpublic string id;\n\tpublic float weight;\n}\n\npublic WeightedItem SelectItem () {\n\t// Prepare weighted items.\n\tvar items = new WeightedItem[] {\n\t\tnew WeightedItem { id = \"🍒\", weight = 8f },\n\t\tnew WeightedItem { id = \"🍏\", weight = 4f },\n\t\tnew WeightedItem { id = \"🍍\", weight = 0f },\n\t\tnew WeightedItem { id = \"🍇\", weight = 6f },\n\t\tnew WeightedItem { id = \"🍊\", weight = -1f }\n\t};\n\t\n\t// Create the WeightedSelector.\n\tvar weightedSelector = items.ToWeightedSelector(item =\u003e item.weight);\n\t\n\t// The probability of each item being selected,\n\t// 🍒 is 44%, 🍏 is 22%, and 🍇 is 33%.\n\t// 🍍 and 🍊 will never be selected because their weights are less or equal to 0.\n\treturn weightedSelector.SelectItemWithUnityRandom();\n\t// Same as weightedSelector.SelectItem(UnityEngine.Random.value);\n}\n```\n\n\n## \u003ca id=\"toweightedselector-overloads\" href=\"#toweightedselector-overloads\"\u003e `ToWeightedSelector` Overloads  \u003c/a\u003e\n\nThe `ToWeightedSelector` method has many overloads and can be used for a variety of patterns.\n\n### \u003ca id=\"from-weighted-entry\" href=\"#from-weighted-entry\"\u003e from weighted entry pattern \u003c/a\u003e\n\n```cs\npublic struct ItemEntry {\n\tpublic Item item;\n\tpublic float weight;\n}\n\npublic IWeightedSelector\u003cItem\u003e WeightedEntryPattern () {\n\tvar entries = new ItemEntry[] {\n\t\tnew ItemEntry { item = new Item { id = \"🍒\" }, weight = 1f },\n\t\tnew ItemEntry { item = new Item { id = \"🍏\" }, weight = 5f },\n\t\tnew ItemEntry { item = new Item { id = \"🍍\" }, weight = 3f }\n\t};\n\n\t// Create a WeightedSelector by selecting item and weight from entry respectively.\n\treturn entries.ToWeightedSelector(\n\t\titemSelector: entry =\u003e entry.item,\n\t\tweightSelector: entry =\u003e entry.weight\n\t);\n}\n```\n\n### \u003ca id=\"from-weighted-item\" href=\"#from-weighted-item\"\u003e from weighted item pattern \u003c/a\u003e\n\n```cs\npublic class WeightedItem {\n\tpublic string id;\n\tpublic float weight;\n}\n\npublic IWeightedSelector\u003cWeightedItem\u003e WeightedItemPattern () {\n\tvar items = new WeightedItem[] {\n\t\tnew WeightedItem { id = \"🍒\", weight = 1f },\n\t\tnew WeightedItem { id = \"🍏\", weight = 5f },\n\t\tnew WeightedItem { id = \"🍍\", weight = 3f }\n\t};\n\n\t// Create a WeightedSelector using the weight of the WeightedItem.\n\treturn fromWeightedItem = items.ToWeightedSelector(weightSelector: item =\u003e item.weight);\n}\n```\n\n### \u003ca id=\"from-dictionary\" href=\"#from-dictionary\"\u003e from `Dictionary\u003cTItem,float\u003e` pattern \u003c/a\u003e\n\n```cs\npublic class Item {\n\tpublic string id;\n}\n\npublic IWeightedSelector\u003cItem\u003e DictionaryPattern () {\n\t// This need a Dictionary\u003cTItem,float\u003e. (Strictly speaking, IEnumerable\u003cKeyValuePair\u003cTItem,float\u003e\u003e)\n\tvar dictionary = new Dictionary\u003cItem,float\u003e(\n\t\t{ new Item { id = \"🍒\" }, 1f },\n\t\t{ new Item { id = \"🍏\" }, 5f },\n\t\t{ new Item { id = \"🍍\" }, 3f }\n\t);\n\n\t// Create a WeightedSelector with the dictionary key as item and value as weight.\n\treturn dictionary.ToWeightedSelector();\n}\n```\n\n\n## \u003ca id=\"linq\" href=\"#linq\"\u003e LINQ \u003c/a\u003e\n\nSince the `ToWeightedSelector` method is defined as an extension of `IEnumerable\u003cT\u003e`, it can be connected from the LINQ query operators.\n\n```cs\nvar randomSelectedItem = items\n\t.Where(item =\u003e item != null) // null check\n\t.ToWeightedSelector(item =\u003e item.weight)\n\t.SelectItemWithUnityRandom();\n```\n\n\n## \u003ca id=\"algorithms\" href=\"#algorithms\"\u003e Algorithms \u003c/a\u003e\n\nWhen creating a WeightedSelector, you can specify the `IWeightedSelectMethod`.\n\n```cs\nvar weightedSelector = items.ToWeightedSelector(\n\titem =\u003e item.weight,\n\tWeightedSelectMethod.Binary // Use the binary search algorithm.\n);\n```\n\nAll `ToWeightedSelector` methods can specify `IWeightedSelectMethod`.\n\nIf this is not specified, the linear scan algorithm will be used automatically.\n\n\n### \u003ca id=\"linear-scan\" href=\"#linear-scan\"\u003e Linear Scan (`WeightedSelectMethod.Linear`) \u003c/a\u003e\n\nThe simplest algorithm that walks linearly along the weights.\nThis method is an `O(n)` operation, where `n` is number of weights.\n\n\n### \u003ca id=\"binary-search\" href=\"#binary-search\"\u003e Binary Search (`WeightedSelectMethod.Binary`) \u003c/a\u003e\n\nThe binary search algorithm that is faster than linear scan by preprocessing to store the current sum of weights.\n\nIt has an additional storage cost of `O(n)`, but is accelerated by up to `O(log(n))` for each selection, where `n` is number of weights.\n\n\n### \u003ca id=\"alias-method\" href=\"#alias-method\"\u003e Alias Method (`WeightedSelectMethod.Alias`) \u003c/a\u003e\n\nThe fastest algorithm.\n\nIt takes `O(n)` run time to set up, but the selection is performed in `O(1)` run time,\nwhere `n` is number of weights.\n\nTherefore, this is a very effective algorithm for selecting multiple items.\n\n## \u003ca id=\"speed-measurement\" href=\"#speed-measurement\"\u003e Speed Measurement \u003c/a\u003e\n\n### \u003ca id=\"1-items\" href=\"#1-items\"\u003e 1 items \u003c/a\u003e\n\n![Speed measurement of Weighted Random Selection Algorithms  (1 items)](https://user-images.githubusercontent.com/13536348/127739858-60f05a16-6e3b-42f6-b7f2-b4b106eb3dfa.png)\n\n|Iterations|1|10|100|1000|10000|\n|:--|:--|:--|:--|:--|:--|\n|Linear Scan|0.0104ms|**0.0055ms**|**0.0081ms**|**0.0393ms**|**0.3408ms**|\n|Binary Search|0.0091ms|0.0083ms|0.0126ms|0.0659ms|0.5944ms|\n|Alias Method|**0.0069ms**|0.0065ms|0.01ms|0.0459ms|0.4094ms|\n\n|Iterations|1|10|100|1000|10000|\n|:--|:--|:--|:--|:--|:--|\n|Linear Scan|0.0155ms|**0.0064ms**|**0.0077ms**|**0.0381ms**|**0.353ms**|\n|Binary Search|0.0077ms|0.008ms|0.0123ms|0.0659ms|0.5919ms|\n|Alias Method|**0.0062ms**|0.0065ms|0.01ms|0.0462ms|0.41ms|\n\n|Iterations|1|10|100|1000|10000|\n|:--|:--|:--|:--|:--|:--|\n|Linear Scan|0.009ms|**0.0053ms**|**0.0081ms**|**0.0378ms**|**0.3388ms**|\n|Binary Search|0.0073ms|0.0079ms|0.0129ms|0.0653ms|0.5927ms|\n|Alias Method|**0.0054ms**|0.0062ms|0.0104ms|0.0461ms|0.4194ms|\n\n\n### \u003ca id=\"10-items\" href=\"#10-items\"\u003e 10 items \u003c/a\u003e\n\n![Speed measurement of Weighted Random Selection Algorithms  (10 items)](https://user-images.githubusercontent.com/13536348/127739862-bddbf2d2-6075-4d4e-bfc7-9ccd2f8fdbb3.png)\n\n|Iterations|1|10|100|1000|10000|\n|:--|:--|:--|:--|:--|:--|\n|Linear Scan|0.0113ms|**0.0077ms**|0.0182ms|0.1219ms|1.19ms|\n|Binary Search|**0.0109ms**|0.0114ms|0.0237ms|0.158ms|1.4975ms|\n|Alias Method|0.0136|0.022ms|**0.0151ms**|**0.0601ms**|**0.5041ms**|\n\n|Iterations|1|10|100|1000|10000|\n|:--|:--|:--|:--|:--|:--|\n|Linear Scan|0.012ms|**0.0072ms**|0.0174ms|0.1272ms|1.1738ms|\n|Binary Search|**0.0095ms**|0.0099ms|0.023ms|0.16ms|1.5503ms|\n|Alias Method|0.0141ms|0.0104ms|**0.0148ms**|**0.0618ms**|**0.5235ms**|\n\n|Iterations|1|10|100|1000|10000|\n|:--|:--|:--|:--|:--|:--|\n|Linear Scan|**0.0095ms**|**0.009ms**|0.0179ms|0.1216ms|1.1503ms|\n|Binary Search|0.0096ms|0.0103ms|0.0225ms|0.1572ms|1.4991ms|\n|Alias Method|0.0129ms|0.0105ms|**0.015ms**|**0.0607ms**|**0.5176ms**|\n\n### \u003ca id=\"100-items\" href=\"#100-items\"\u003e 100 items \u003c/a\u003e\n\n![Speed measurement of Weighted Random Selection Algorithms  (100 items)](https://user-images.githubusercontent.com/13536348/127739863-d9a3338b-1a40-45bb-a292-9330b9414561.png)\n\n|Iterations|1|10|100|1000|10000|\n|:--|:--|:--|:--|:--|:--|\n|Linear Scan|**0.0201ms**|0.024ms|0.0822ms|0.741ms|7.2211ms|\n|Binary Search|0.0212ms|**0.0211ms**|0.0433ms|0.3118ms|2.6434ms|\n|Alias Method|0.0717ms|0.0364ms|**0.0395ms**|**0.086ms**|**0.5462ms**|\n\n|Iterations|1|10|100|1000|10000|\n|:--|:--|:--|:--|:--|:--|\n|Linear Scan|0.0231ms|0.0247ms|0.0855ms|0.7027ms|7.0025ms|\n|Binary Search|**0.0224ms**|**0.0231ms**|0.0441ms|0.2776ms|2.6521ms|\n|Alias Method|*0.039ms|0.0358ms|**0.0405ms**|**0.0861ms**|**0.5561ms**|\n\n|Iterations|1|10|100|1000|10000|\n|:--|:--|:--|:--|:--|:--|\n|Linear Scan|**0.0194ms**|0.0232ms|0.0892ms|0.7582ms|7.1886ms|\n|Binary Search|0.0206ms|**0.0218ms**|0.0447ms|0.2804ms|2.6375ms|\n|Alias Method|0.0376ms|0.0381ms|**0.0413ms**|**0.0871ms**|**0.5728ms**|\n\n### \u003ca id=\"1000-items\" href=\"#1000-items\"\u003e 1000 items \u003c/a\u003e\n\n![Speed measurement of Weighted Random Selection Algorithms  (1000 items)](https://user-images.githubusercontent.com/13536348/127739880-c2da6789-126b-4d8b-8182-f3ee8f9936d5.png)\n\n|Iterations|1|10|100|1000|10000|\n|:--|:--|:--|:--|:--|:--|\n|Linear Scan|**0.1147ms**|0.1672ms|0.7792ms|6.7539ms|66.8329ms|\n|Binary Search|0.1205ms|**0.1183ms**|**0.1504ms**|0.4758ms|3.7755ms|\n|Alias Method|0.2783ms|0.2722ms|0.2925ms|**0.3238ms**|**0.7824ms**|\n\n|Iterations|1|10|100|1000|10000|\n|:--|:--|:--|:--|:--|:--|\n|Linear Scan|**0.1068ms**|0.1717ms|0.8331ms|6.8282ms|68.455ms|\n|Binary Search|0.1217ms|**0.1173ms**|**0.1499ms**|0.5026ms|3.7627ms|\n|Alias Method|0.2785ms|0.2889ms|0.2876ms|**0.3318ms**|**0.908ms**|\n\n|Iterations|1|10|100|1000|10000|\n|:--|:--|:--|:--|:--|:--|\n|Linear Scan|**0.102ms**|0.1636ms|0.7271ms|6.743ms|66.4393ms|\n|Binary Search|0.1241ms|**0.1208ms**|**0.1501ms**|0.5216ms|4.0165ms|\n|Alias Method|0.2782ms|0.2755ms|0.2777ms|**0.3454ms**|**0.8068ms**|\n\n\n### \u003ca id=\"10000-items\" href=\"#10000-items\"\u003e 10000 items \u003c/a\u003e\n\n![Speed measurement of Weighted Random Selection Algorithms  (10000 items)](https://user-images.githubusercontent.com/13536348/127739886-c0e4bbea-f3cc-4ece-9abe-6eed68597f0a.png)\n\n|Iterations|1|10|100|1000|10000|\n|:--|:--|:--|:--|:--|:--|\n|Linear Scan|**1.1885ms**|1.7971ms|8.0482ms|69.1749ms|664.8696ms|\n|Binary Search|1.3329ms|**1.3181ms**|**1.3454ms**|**1.7735ms**|6.1215ms|\n|Alias Method|2.8859ms|2.8719ms|2.8832ms|2.9779ms|**3.4764ms**|\n\n|Iterations|1|10|100|1000|10000|\n|:--|:--|:--|:--|:--|:--|\n|Linear Scan|**1.1676ms**|1.6953ms|8.0905ms|70.1629ms|668.3197ms|\n|Binary Search|1.3118ms|**1.3361ms**|**1.3407ms**|**1.786ms**|6.1105ms|\n|Alias Method|2.8833ms|2.934ms|2.951ms|2.9845ms|**3.6259ms**|\n\n|Iterations|1|10|100|1000|10000|\n|:--|:--|:--|:--|:--|:--|\n|Linear Scan|**1.3098ms**|1.9826ms|8.063ms|68.9301ms|666.9364ms|\n|Binary Search|1.4456ms|**1.3787ms**|4.6233ms|**1.7861ms**|6.0783ms|\n|Alias Method|2.9751ms|2.9144ms|**2.9236ms**|2.9851ms|**3.5149ms**|\n\n### \u003ca id=\"10000-items\" href=\"#10000-items\"\u003e 10000 items without setup \u003c/a\u003e\n\n![Speed measurement of Weighted Random Selection Algorithms  (10000 items without setup)](https://user-images.githubusercontent.com/13536348/127739890-3d5a1cd3-5b93-4151-a29d-c5e48507b15c.png)\n\n|Iterations|1|10|100|1000|10000|\n|:--|:--|:--|:--|:--|:--|\n|Linear Scan|0.0207ms|0.7364ms|6.5433ms|67.3963ms|671.3184ms|\n|Binary Search|0.0015ms|0.0055ms|0.0492ms|0.496ms|4.828ms|\n|Alias Method|**0.0005ms**|**0.0011ms**|**0.0066ms**|**0.0579ms**|**0.5559ms**|\n\n# \u003ca id=\"author-info\" href=\"#author-info\"\u003e 📔 Author Info \u003c/a\u003e\n\nHiroya Aramaki is a indie game developer in Japan.\n\n- Blog: [https://mackysoft.net/blog](https://mackysoft.net/blog)\n- Twitter: [https://twitter.com/makihiro_dev](https://twitter.com/makihiro_dev)\n\n\n# \u003ca id=\"license\" href=\"#license\"\u003e 📜 License \u003c/a\u003e\n\nThis library is under the MIT License.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmackysoft%2Fchoice","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmackysoft%2Fchoice","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmackysoft%2Fchoice/lists"}