├── .github └── workflows │ └── build.yml ├── .gitignore ├── Benchmarks ├── Benchmarks.csp_roj └── Program.cs ├── Combinatorics.sln ├── LICENSE ├── README.md ├── src ├── Combinatorics │ ├── Combinations.cs │ ├── Combinatorics.csproj │ ├── GenerateOption.cs │ ├── Permutations.cs │ ├── SmallPrimeUtility.cs │ └── Variations.cs ├── Directory.Build.props ├── Directory.Build.targets ├── icon.png └── project.props └── test ├── Directory.Build.props └── UnitTests ├── CombinatoricTests.cs └── UnitTests.csproj /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # build.yml v1.4 2 | # 1.4 - Avoid set-env. 3 | # 1.3 - Include tag workflow in this file. 4 | # 1.2 - Define DOTNET_SKIP_FIRST_TIME_EXPERIENCE/NUGET_XMLDOC_MODE. 5 | # 1.1 - Use actions/cache@v2. 6 | # 1.0 - Initial release. 7 | 8 | name: Build 9 | 10 | on: 11 | - push 12 | 13 | env: 14 | CI: 'true' 15 | DOTNET_CLI_TELEMETRY_OPTOUT: 'true' 16 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 'true' 17 | NUGET_XMLDOC_MODE: 'skip' 18 | 19 | jobs: 20 | build: 21 | runs-on: ubuntu-latest 22 | steps: 23 | 24 | - name: Dump context 25 | env: 26 | CONTEXT: ${{ toJson(github) }} 27 | run: | 28 | echo "Context: $Env:CONTEXT" 29 | 30 | - name: Determine cache fallbacks 31 | if: github.event_name != 'push' 32 | id: cache_fallbacks 33 | run: | 34 | echo "::set-output name=nuget::nuget-" 35 | 36 | - name: Checkout 37 | uses: actions/checkout@v2 38 | with: 39 | fetch-depth: 0 40 | 41 | - name: Cache nuget 42 | uses: actions/cache@v2 43 | with: 44 | path: ~/.nuget/packages 45 | key: nuget-${{ hashFiles('**/*.csproj') }}-${{ hashFiles('**/*.props') }} 46 | restore-keys: ${{ steps.cache_fallbacks.outputs.nuget }} 47 | 48 | - name: Get existing tag 49 | id: existingtag 50 | uses: WyriHaximus/github-action-get-previous-tag@0.2.0 51 | continue-on-error: true 52 | 53 | - name: Get current version 54 | run: | 55 | dotnet tool install --global Nito.ProjProps 56 | echo "NEWTAG=v$(projprops --name version --output-format SingleValueOnly --project src --project-search)" >> $GITHUB_ENV 57 | 58 | - name: Build 59 | run: | 60 | dotnet build --configuration Release 61 | dotnet test --configuration Release --no-build --collect:"XPlat Code Coverage" 62 | dotnet pack --configuration Release --no-build 63 | 64 | - name: Upload package artifacts 65 | uses: actions/upload-artifact@v2 66 | with: 67 | name: nuget-packages 68 | path: | 69 | **/*.nupkg 70 | **/*.snupkg 71 | 72 | - name: Publish code coverage 73 | uses: codecov/codecov-action@v1 74 | 75 | - name: Publish packages 76 | if: env.NEWTAG != steps.existingtag.outputs.tag 77 | run: | 78 | dotnet nuget push **/*.nupkg --source https://api.nuget.org/v3/index.json --skip-duplicate --api-key ${{ secrets.NUGET_API_KEY }} 79 | 80 | - name: Create tag 81 | if: env.NEWTAG != steps.existingtag.outputs.tag 82 | run: | 83 | git tag ${{ env.NEWTAG }} 84 | git push --tags 85 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # build-tools output 2 | Bundle 3 | Publish 4 | 5 | ## Ignore Visual Studio temporary files, build results, and 6 | ## files generated by popular Visual Studio add-ons. 7 | 8 | # User-specific files 9 | *.suo 10 | *.user 11 | *.userosscache 12 | *.sln.docstates 13 | 14 | # User-specific files (MonoDevelop/Xamarin Studio) 15 | *.userprefs 16 | 17 | # Build results 18 | [Dd]ebug/ 19 | [Dd]ebugPublic/ 20 | [Rr]elease/ 21 | [Rr]eleases/ 22 | x64/ 23 | x86/ 24 | build/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | 29 | # Visual Studio 2015 cache/options directory 30 | .vs/ 31 | 32 | # SonarQube 33 | .sonarqube 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # DNX 49 | project.lock.json 50 | artifacts/ 51 | 52 | *_i.c 53 | *_p.c 54 | *_i.h 55 | *.ilk 56 | *.meta 57 | *.obj 58 | *.pch 59 | *.pdb 60 | *.pgc 61 | *.pgd 62 | *.rsp 63 | *.sbr 64 | *.tlb 65 | *.tli 66 | *.tlh 67 | *.tmp 68 | *.tmp_proj 69 | *.log 70 | *.vspscc 71 | *.vssscc 72 | .builds 73 | *.pidb 74 | *.svclog 75 | *.scc 76 | 77 | # Chutzpah Test files 78 | _Chutzpah* 79 | 80 | # Visual C++ cache files 81 | ipch/ 82 | *.aps 83 | *.ncb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | 88 | # Visual Studio profiler 89 | *.psess 90 | *.vsp 91 | *.vspx 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | 117 | # MightyMoose 118 | *.mm.* 119 | AutoTest.Net/ 120 | 121 | # Web workbench (sass) 122 | .sass-cache/ 123 | 124 | # Installshield output folder 125 | [Ee]xpress/ 126 | 127 | # DocProject is a documentation generator add-in 128 | DocProject/buildhelp/ 129 | DocProject/Help/*.HxT 130 | DocProject/Help/*.HxC 131 | DocProject/Help/*.hhc 132 | DocProject/Help/*.hhk 133 | DocProject/Help/*.hhp 134 | DocProject/Help/Html2 135 | DocProject/Help/html 136 | 137 | # Click-Once directory 138 | publish/ 139 | 140 | # Publish Web Output 141 | *.[Pp]ublish.xml 142 | *.azurePubxml 143 | ## TODO: Comment the next line if you want to checkin your 144 | ## web deploy settings but do note that will include unencrypted 145 | ## passwords 146 | #*.pubxml 147 | 148 | *.publishproj 149 | 150 | # NuGet Packages 151 | *.nupkg 152 | # The packages folder can be ignored because of Package Restore 153 | **/packages/* 154 | # except build/, which is used as an MSBuild target. 155 | !**/packages/build/ 156 | # Uncomment if necessary however generally it will be regenerated when needed 157 | #!**/packages/repositories.config 158 | 159 | # Windows Azure Build Output 160 | csx/ 161 | *.build.csdef 162 | 163 | # Windows Store app package directory 164 | AppPackages/ 165 | 166 | # Visual Studio cache files 167 | # files ending in .cache can be ignored 168 | *.[Cc]ache 169 | # but keep track of directories ending in .cache 170 | !*.[Cc]ache/ 171 | 172 | # Others 173 | ClientBin/ 174 | [Ss]tyle[Cc]op.* 175 | ~$* 176 | *~ 177 | *.dbmdl 178 | *.dbproj.schemaview 179 | *.pfx 180 | *.publishsettings 181 | node_modules/ 182 | orleans.codegen.cs 183 | 184 | # RIA/Silverlight projects 185 | Generated_Code/ 186 | 187 | # Backup & report files from converting an old project file 188 | # to a newer Visual Studio version. Backup files are not needed, 189 | # because we have git ;-) 190 | _UpgradeReport_Files/ 191 | Backup*/ 192 | UpgradeLog*.XML 193 | UpgradeLog*.htm 194 | 195 | # SQL Server files 196 | *.mdf 197 | *.ldf 198 | 199 | # Business Intelligence projects 200 | *.rdl.data 201 | *.bim.layout 202 | *.bim_*.settings 203 | 204 | # Microsoft Fakes 205 | FakesAssemblies/ 206 | 207 | # Node.js Tools for Visual Studio 208 | .ntvs_analysis.dat 209 | 210 | # Visual Studio 6 build log 211 | *.plg 212 | 213 | # Visual Studio 6 workspace options file 214 | *.opt 215 | 216 | # LightSwitch generated files 217 | GeneratedArtifacts/ 218 | _Pvt_Extensions/ 219 | ModelManifest.xml 220 | -------------------------------------------------------------------------------- /Benchmarks/Benchmarks.csp_roj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net48;netcoreapp3.1 6 | AnyCPU 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Benchmarks/Program.cs: -------------------------------------------------------------------------------- 1 | extern alias localbuild; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Numerics; 6 | using BenchmarkDotNet.Attributes; 7 | using BenchmarkDotNet.Configs; 8 | using BenchmarkDotNet.Diagnosers; 9 | using BenchmarkDotNet.Order; 10 | using BenchmarkDotNet.Running; 11 | using NugetCombinatorics = Combinatorics.Collections; 12 | using LocalCombinatorics = localbuild::Combinatorics.Collections; 13 | 14 | namespace Benchmarks 15 | { 16 | public class PermutationBenchmarks 17 | { 18 | private List _source; 19 | private List _largeSource; 20 | private List _veryLargeSource; 21 | private BigInteger _veryLargeSourceCount; 22 | 23 | [GlobalSetup] 24 | public void GlobalSetup() 25 | { 26 | _source = Enumerable.Range(0, 9).ToList(); 27 | _largeSource = Enumerable.Range(0, 19).ToList(); 28 | _veryLargeSource = Enumerable.Range(0, 21).ToList(); 29 | _veryLargeSourceCount = BigInteger.Parse("51090942171709440000"); 30 | } 31 | 32 | [BenchmarkCategory("Enumerate"), Benchmark(Baseline = true)] 33 | public void EnumerateOld() 34 | { 35 | var permutations = new NugetCombinatorics.Permutations(_source); 36 | foreach (var p in permutations) 37 | ; 38 | } 39 | 40 | [BenchmarkCategory("Enumerate"), Benchmark] 41 | public void EnumerateNew() 42 | { 43 | var permutations = new LocalCombinatorics.Permutations(_source); 44 | foreach (var p in permutations) 45 | ; 46 | } 47 | 48 | [BenchmarkCategory("Count0"), Benchmark(Baseline = true)] 49 | public void Count0Old() 50 | { 51 | var permutations = new NugetCombinatorics.Permutations(_source); 52 | if (permutations.Count != 362880) 53 | throw new InvalidOperationException($"Expected 362880 but got {permutations.Count}."); 54 | } 55 | 56 | [BenchmarkCategory("Count0"), Benchmark] 57 | public void Count0New() 58 | { 59 | var permutations = new LocalCombinatorics.Permutations(_source); 60 | if (permutations.Count != 362880) 61 | throw new InvalidOperationException($"Expected 362880 but got {permutations.Count}."); 62 | } 63 | 64 | [BenchmarkCategory("Count1"), Benchmark(Baseline = true)] 65 | public void Count1Old() 66 | { 67 | var permutations = new NugetCombinatorics.Permutations(_largeSource); 68 | if (permutations.Count != 121645100408832000) 69 | throw new InvalidOperationException($"Expected 121645100408832000 but got {permutations.Count}."); 70 | } 71 | 72 | [BenchmarkCategory("Count1"), Benchmark] 73 | public void Count1New() 74 | { 75 | var permutations = new LocalCombinatorics.Permutations(_largeSource); 76 | if (permutations.Count != 121645100408832000) 77 | throw new InvalidOperationException($"Expected 121645100408832000 but got {permutations.Count}."); 78 | } 79 | 80 | [BenchmarkCategory("Count2"), Benchmark(Baseline = true)] 81 | public void Count2Old() 82 | { 83 | var permutations = new NugetCombinatorics.Permutations(_veryLargeSource); 84 | if (permutations.Count != _veryLargeSourceCount) 85 | throw new InvalidOperationException($"Expected 51090942171709440000 but got {permutations.Count}."); 86 | } 87 | 88 | [BenchmarkCategory("Count2"), Benchmark] 89 | public void Count2New() 90 | { 91 | var permutations = new LocalCombinatorics.Permutations(_veryLargeSource); 92 | if (permutations.Count != _veryLargeSourceCount) 93 | throw new InvalidOperationException($"Expected 51090942171709440000 but got {permutations.Count}."); 94 | } 95 | } 96 | 97 | class Program 98 | { 99 | static void Main(string[] args) 100 | { 101 | BenchmarkRunner.Run(typeof(Program).Assembly, 102 | DefaultConfig.Instance 103 | .WithOptions(ConfigOptions.JoinSummary | ConfigOptions.DisableOptimizationsValidator) 104 | .AddDiagnoser(MemoryDiagnoser.Default) 105 | .WithOrderer(new DefaultOrderer(SummaryOrderPolicy.Method)) 106 | .AddLogicalGroupRules(BenchmarkLogicalGroupRule.ByCategory) 107 | ); 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Combinatorics.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30517.126 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests", "test\UnitTests\UnitTests.csproj", "{243B4F2A-E25A-4151-8AB2-5E89341B8E2A}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Combinatorics", "src\Combinatorics\Combinatorics.csproj", "{0A6FE84B-697A-4C83-9083-D9A94F2A63BE}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_misc", "_misc", "{2C6D5001-6B68-4061-9376-1415D2CBADC2}" 11 | ProjectSection(SolutionItems) = preProject 12 | .gitignore = .gitignore 13 | LICENSE = LICENSE 14 | README.md = README.md 15 | EndProjectSection 16 | EndProject 17 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{6CC8DF54-815B-4B4A-A590-8834AD530683}" 18 | ProjectSection(SolutionItems) = preProject 19 | .github\workflows\build.yml = .github\workflows\build.yml 20 | EndProjectSection 21 | EndProject 22 | Global 23 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 24 | Debug|Any CPU = Debug|Any CPU 25 | Debug|x64 = Debug|x64 26 | Debug|x86 = Debug|x86 27 | Release|Any CPU = Release|Any CPU 28 | Release|x64 = Release|x64 29 | Release|x86 = Release|x86 30 | EndGlobalSection 31 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 32 | {243B4F2A-E25A-4151-8AB2-5E89341B8E2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {243B4F2A-E25A-4151-8AB2-5E89341B8E2A}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {243B4F2A-E25A-4151-8AB2-5E89341B8E2A}.Debug|x64.ActiveCfg = Debug|Any CPU 35 | {243B4F2A-E25A-4151-8AB2-5E89341B8E2A}.Debug|x64.Build.0 = Debug|Any CPU 36 | {243B4F2A-E25A-4151-8AB2-5E89341B8E2A}.Debug|x86.ActiveCfg = Debug|Any CPU 37 | {243B4F2A-E25A-4151-8AB2-5E89341B8E2A}.Debug|x86.Build.0 = Debug|Any CPU 38 | {243B4F2A-E25A-4151-8AB2-5E89341B8E2A}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {243B4F2A-E25A-4151-8AB2-5E89341B8E2A}.Release|Any CPU.Build.0 = Release|Any CPU 40 | {243B4F2A-E25A-4151-8AB2-5E89341B8E2A}.Release|x64.ActiveCfg = Release|Any CPU 41 | {243B4F2A-E25A-4151-8AB2-5E89341B8E2A}.Release|x64.Build.0 = Release|Any CPU 42 | {243B4F2A-E25A-4151-8AB2-5E89341B8E2A}.Release|x86.ActiveCfg = Release|Any CPU 43 | {243B4F2A-E25A-4151-8AB2-5E89341B8E2A}.Release|x86.Build.0 = Release|Any CPU 44 | {0A6FE84B-697A-4C83-9083-D9A94F2A63BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 | {0A6FE84B-697A-4C83-9083-D9A94F2A63BE}.Debug|Any CPU.Build.0 = Debug|Any CPU 46 | {0A6FE84B-697A-4C83-9083-D9A94F2A63BE}.Debug|x64.ActiveCfg = Debug|Any CPU 47 | {0A6FE84B-697A-4C83-9083-D9A94F2A63BE}.Debug|x64.Build.0 = Debug|Any CPU 48 | {0A6FE84B-697A-4C83-9083-D9A94F2A63BE}.Debug|x86.ActiveCfg = Debug|Any CPU 49 | {0A6FE84B-697A-4C83-9083-D9A94F2A63BE}.Debug|x86.Build.0 = Debug|Any CPU 50 | {0A6FE84B-697A-4C83-9083-D9A94F2A63BE}.Release|Any CPU.ActiveCfg = Release|Any CPU 51 | {0A6FE84B-697A-4C83-9083-D9A94F2A63BE}.Release|Any CPU.Build.0 = Release|Any CPU 52 | {0A6FE84B-697A-4C83-9083-D9A94F2A63BE}.Release|x64.ActiveCfg = Release|Any CPU 53 | {0A6FE84B-697A-4C83-9083-D9A94F2A63BE}.Release|x64.Build.0 = Release|Any CPU 54 | {0A6FE84B-697A-4C83-9083-D9A94F2A63BE}.Release|x86.ActiveCfg = Release|Any CPU 55 | {0A6FE84B-697A-4C83-9083-D9A94F2A63BE}.Release|x86.Build.0 = Release|Any CPU 56 | EndGlobalSection 57 | GlobalSection(SolutionProperties) = preSolution 58 | HideSolutionNode = FALSE 59 | EndGlobalSection 60 | GlobalSection(NestedProjects) = preSolution 61 | {6CC8DF54-815B-4B4A-A590-8834AD530683} = {2C6D5001-6B68-4061-9376-1415D2CBADC2} 62 | EndGlobalSection 63 | GlobalSection(ExtensibilityGlobals) = postSolution 64 | SolutionGuid = {ADE7A58C-3553-4BAC-8014-60C27B0617FF} 65 | EndGlobalSection 66 | EndGlobal 67 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2021 Combinatorics Authors 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build status](https://ci.appveyor.com/api/projects/status/tr38vj9ebhokwsyi?svg=true)](https://ci.appveyor.com/project/eoincampbell/combinatorics) 2 | 3 | This project contains the Combinatorics Implementations of Adrian Akison, taken from his excellent CodeProject Article 4 | http://www.codeproject.com/Articles/26050/Permutations-Combinations-and-Variations-using-C-G 5 | 6 | 7 | "Combinatorics has many applications within computer science for solving complex problems. 8 | However, it is under-represented in libraries since there is little application of Combinatorics in business applications. 9 | Fortunately, the science behind it has been studied by mathematicians for centuries, and is well understood and well documented. 10 | However, mathematicians are focused on how many elements will exist within a Combinatorics problem, and have little interest in actually going through the work of creating those lists. Enter computer science to actually construct these massive collections." 11 | 12 | You can [install the package from Nuget](https://www.nuget.org/packages/Nito.Combinatorics/) 13 | 14 | > PM> Install-Package Combinatorics 15 | -------------------------------------------------------------------------------- /src/Combinatorics/Combinations.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Numerics; 6 | 7 | namespace Combinatorics.Collections 8 | { 9 | /// 10 | /// Combinations defines a sequence of all possible subsets of a particular size from the set of values. 11 | /// Within the returned set, there is no prescribed order. 12 | /// This follows the mathematical concept of choose. 13 | /// For example, put 10 dominoes in a hat and pick 5. 14 | /// The number of possible combinations is defined as "10 choose 5", which is calculated as (10!) / ((10 - 5)! * 5!). 15 | /// 16 | /// 17 | /// The MetaCollectionType parameter of the constructor allows for the creation of 18 | /// two types of sets, those with and without repetition in the output set when 19 | /// presented with repetition in the input set. 20 | /// 21 | /// When given a input collect {A B C} and lower index of 2, the following sets are generated: 22 | /// MetaCollectionType.WithRepetition => 23 | /// {A A}, {A B}, {A C}, {B B}, {B C}, {C C} 24 | /// MetaCollectionType.WithoutRepetition => 25 | /// {A B}, {A C}, {B C} 26 | /// 27 | /// Input sets with multiple equal values will generate redundant combinations in proportion 28 | /// to the likelihood of outcome. For example, {A A B B} and a lower index of 3 will generate: 29 | /// {A A B} {A A B} {A B B} {A B B} 30 | /// 31 | /// The type of the values within the list. 32 | public sealed class Combinations : IEnumerable> 33 | { 34 | /// 35 | /// Create a combination set from the provided list of values. 36 | /// The upper index is calculated as values.Count, the lower index is specified. 37 | /// Collection type defaults to MetaCollectionType.WithoutRepetition 38 | /// 39 | /// List of values to select combinations from. 40 | /// The size of each combination set to return. 41 | public Combinations(IEnumerable values, int lowerIndex) 42 | : this(values, lowerIndex, GenerateOption.WithoutRepetition) 43 | { 44 | } 45 | 46 | /// 47 | /// Create a combination set from the provided list of values. 48 | /// The upper index is calculated as values.Count, the lower index is specified. 49 | /// 50 | /// List of values to select combinations from. 51 | /// The size of each combination set to return. 52 | /// The type of Combinations set to generate. 53 | public Combinations(IEnumerable values, int lowerIndex, GenerateOption type) 54 | { 55 | _ = values ?? throw new ArgumentNullException(nameof(values)); 56 | 57 | // Copy the array and parameters and then create a map of booleans that will 58 | // be used by a permutations object to reference the subset. This map is slightly 59 | // different based on whether the type is with or without repetition. 60 | // 61 | // When the type is WithoutRepetition, then a map of upper index elements is 62 | // created with lower index false's. 63 | // E.g. 8 choose 3 generates: 64 | // Map: {1 1 1 1 1 0 0 0} 65 | // Note: For sorting reasons, false denotes inclusion in output. 66 | // 67 | // When the type is WithRepetition, then a map of upper index - 1 + lower index 68 | // elements is created with the falses indicating that the 'current' element should 69 | // be included and the trues meaning to advance the 'current' element by one. 70 | // E.g. 8 choose 3 generates: 71 | // Map: {1 1 1 1 1 1 1 1 0 0 0} (7 trues, 3 falses). 72 | 73 | Type = type; 74 | LowerIndex = lowerIndex; 75 | _myValues = values.ToList(); 76 | List myMap; 77 | if (type == GenerateOption.WithoutRepetition) 78 | { 79 | myMap = new List(_myValues.Count); 80 | myMap.AddRange(_myValues.Select((t, i) => i < _myValues.Count - LowerIndex)); 81 | } 82 | else 83 | { 84 | myMap = new List(_myValues.Count + LowerIndex - 1); 85 | for (var i = 0; i < _myValues.Count - 1; ++i) 86 | myMap.Add(true); 87 | for (var i = 0; i < LowerIndex; ++i) 88 | myMap.Add(false); 89 | } 90 | 91 | _myPermutations = new Permutations(myMap); 92 | } 93 | 94 | /// 95 | /// Gets an enumerator for collecting the list of combinations. 96 | /// 97 | /// The enumerator. 98 | public IEnumerator> GetEnumerator() => new Enumerator(this); 99 | 100 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 101 | 102 | /// 103 | /// The enumerator that enumerates each meta-collection of the enclosing Combinations class. 104 | /// 105 | public sealed class Enumerator : IEnumerator> 106 | { 107 | /// 108 | /// Construct a enumerator with the parent object. 109 | /// 110 | /// The source combinations object. 111 | public Enumerator(Combinations source) 112 | { 113 | _myParent = source; 114 | _myPermutationsEnumerator = (Permutations.Enumerator)_myParent._myPermutations.GetEnumerator(); 115 | } 116 | 117 | void IEnumerator.Reset() => throw new NotSupportedException(); 118 | 119 | /// 120 | /// Advances to the next combination of items from the set. 121 | /// 122 | /// True if successfully moved to next combination, False if no more unique combinations exist. 123 | /// 124 | /// The heavy lifting is done by the permutations object, the combination is generated 125 | /// by creating a new list of those items that have a true in the permutation parallel array. 126 | /// 127 | public bool MoveNext() 128 | { 129 | var ret = _myPermutationsEnumerator.MoveNext(); 130 | _myCurrentList = null; 131 | return ret; 132 | } 133 | 134 | /// 135 | /// The current combination 136 | /// 137 | public IReadOnlyList Current 138 | { 139 | get 140 | { 141 | ComputeCurrent(); 142 | return _myCurrentList!; 143 | } 144 | } 145 | 146 | object IEnumerator.Current => Current; 147 | 148 | /// 149 | public void Dispose() => _myPermutationsEnumerator.Dispose(); 150 | 151 | /// 152 | /// The only complex function of this entire wrapper, ComputeCurrent() creates 153 | /// a list of original values from the bool permutation provided. 154 | /// The exception for accessing current (InvalidOperationException) is generated 155 | /// by the call to .Current on the underlying enumeration. 156 | /// 157 | /// 158 | /// To compute the current list of values, the underlying permutation object 159 | /// which moves with this enumerator, is scanned differently based on the type. 160 | /// The items have only two values, true and false, which have different meanings: 161 | /// 162 | /// For type WithoutRepetition, the output is a straightforward subset of the input array. 163 | /// E.g. 6 choose 3 without repetition 164 | /// Input array: {A B C D E F} 165 | /// Permutations: {0 1 0 0 1 1} 166 | /// Generates set: {A C D } 167 | /// Note: size of permutation is equal to upper index. 168 | /// 169 | /// For type WithRepetition, the output is defined by runs of characters and when to 170 | /// move to the next element. 171 | /// E.g. 6 choose 5 with repetition 172 | /// Input array: {A B C D E F} 173 | /// Permutations: {0 1 0 0 1 1 0 0 1 1} 174 | /// Generates set: {A B B D D } 175 | /// Note: size of permutation is equal to upper index - 1 + lower index. 176 | /// 177 | private void ComputeCurrent() 178 | { 179 | if (_myCurrentList != null) 180 | return; 181 | 182 | _myCurrentList = new List(_myParent.LowerIndex); 183 | var index = 0; 184 | var currentPermutation = _myPermutationsEnumerator.Current; 185 | foreach (var p in currentPermutation) 186 | { 187 | if (!p) 188 | { 189 | _myCurrentList.Add(_myParent._myValues[index]); 190 | if (_myParent.Type == GenerateOption.WithoutRepetition) 191 | ++index; 192 | } 193 | else 194 | { 195 | ++index; 196 | } 197 | } 198 | } 199 | 200 | /// 201 | /// Parent object this is an enumerator for. 202 | /// 203 | private readonly Combinations _myParent; 204 | 205 | /// 206 | /// The current list of values, this is lazy evaluated by the Current property. 207 | /// 208 | private List? _myCurrentList; 209 | 210 | /// 211 | /// An enumerator of the parents list of lexicographic orderings. 212 | /// 213 | private readonly Permutations.Enumerator _myPermutationsEnumerator; 214 | } 215 | 216 | /// 217 | /// The number of unique combinations that are defined in this meta-collection. 218 | /// This value is mathematically defined as Choose(M, N) where M is the set size 219 | /// and N is the subset size. This is M! / (N! * (M-N)!). 220 | /// 221 | public BigInteger Count => _myPermutations.Count; 222 | 223 | /// 224 | /// The type of Combinations set that is generated. 225 | /// 226 | public GenerateOption Type { get; } 227 | 228 | /// 229 | /// The upper index of the meta-collection, equal to the number of items in the initial set. 230 | /// 231 | public int UpperIndex => _myValues.Count; 232 | 233 | /// 234 | /// The lower index of the meta-collection, equal to the number of items returned each iteration. 235 | /// 236 | public int LowerIndex { get; } 237 | 238 | /// 239 | /// Copy of values object is initialized with, required for enumerator reset. 240 | /// 241 | private readonly List _myValues; 242 | 243 | /// 244 | /// Permutations object that handles permutations on booleans for combination inclusion. 245 | /// 246 | private readonly Permutations _myPermutations; 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /src/Combinatorics/Combinatorics.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard1.1;netstandard2.0;net461 5 | Combinatorics Library for .NET 6 | Apache-2.0 7 | combinatorics;permutation;combination;variation 8 | A combinatorics library providing combinations, permutations & variations. This nuget package wraps up Adrian Akison's original implementation. See: http://www.codeproject.com/Articles/26050/Permutations-Combinations-and-Variations-using-C-G 9 | Combinatorics.Collections 10 | local.Combinatorics 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Combinatorics/GenerateOption.cs: -------------------------------------------------------------------------------- 1 | namespace Combinatorics.Collections 2 | { 3 | /// 4 | /// Indicates whether a permutation, combination or variation generates equivalent result sets. 5 | /// 6 | public enum GenerateOption 7 | { 8 | /// 9 | /// Do not generate equivalent result sets. 10 | /// 11 | WithoutRepetition, 12 | 13 | /// 14 | /// Generate equivalent result sets. 15 | /// 16 | WithRepetition, 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Combinatorics/Permutations.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Numerics; 6 | 7 | namespace Combinatorics.Collections 8 | { 9 | /// 10 | /// Permutations defines a sequence of all possible orderings of a set of values. 11 | /// 12 | /// 13 | /// When given a input collect {A A B}, the following sets are generated: 14 | /// MetaCollectionType.WithRepetition => 15 | /// {A A B}, {A B A}, {A A B}, {A B A}, {B A A}, {B A A} 16 | /// MetaCollectionType.WithoutRepetition => 17 | /// {A A B}, {A B A}, {B A A} 18 | /// 19 | /// When generating non-repetition sets, ordering is based on the lexicographic 20 | /// ordering of the lists based on the provided Comparer. 21 | /// If no comparer is provided, then T must be IComparable on T. 22 | /// 23 | /// When generating repetition sets, no comparisons are performed and therefore 24 | /// no comparer is required and T does not need to be IComparable. 25 | /// 26 | /// The type of the values within the list. 27 | public sealed class Permutations : IEnumerable> 28 | { 29 | /// 30 | /// Create a permutation set from the provided list of values. 31 | /// The values (T) must implement IComparable. 32 | /// If T does not implement IComparable use a constructor with an explicit IComparer. 33 | /// The repetition type defaults to MetaCollectionType.WithholdRepetitionSets 34 | /// 35 | /// List of values to permute. 36 | public Permutations(IEnumerable values) 37 | : this(values, GenerateOption.WithoutRepetition, null) 38 | { 39 | } 40 | 41 | /// 42 | /// Create a permutation set from the provided list of values. 43 | /// If type is MetaCollectionType.WithholdRepetitionSets, then values (T) must implement IComparable. 44 | /// If T does not implement IComparable use a constructor with an explicit IComparer. 45 | /// 46 | /// List of values to permute. 47 | /// The type of permutation set to calculate. 48 | public Permutations(IEnumerable values, GenerateOption type) 49 | : this(values, type, null) 50 | { 51 | } 52 | 53 | /// 54 | /// Create a permutation set from the provided list of values. 55 | /// The values will be compared using the supplied IComparer. 56 | /// The repetition type defaults to MetaCollectionType.WithholdRepetitionSets 57 | /// 58 | /// List of values to permute. 59 | /// Comparer used for defining the lexicographic order. 60 | public Permutations(IEnumerable values, IComparer? comparer) 61 | : this(values, GenerateOption.WithoutRepetition, comparer) 62 | { 63 | } 64 | 65 | /// 66 | /// Create a permutation set from the provided list of values. 67 | /// If type is MetaCollectionType.WithholdRepetitionSets, then the values will be compared using the supplied IComparer. 68 | /// 69 | /// List of values to permute. 70 | /// The type of permutation set to calculate. 71 | /// Comparer used for defining the lexicographic order. 72 | public Permutations(IEnumerable values, GenerateOption type, IComparer? comparer) 73 | { 74 | _ = values ?? throw new ArgumentNullException(nameof(values)); 75 | 76 | // Copy information provided and then create a parallel int array of lexicographic 77 | // orders that will be used for the actual permutation algorithm. 78 | // The input array is first sorted as required for WithoutRepetition and always just for consistency. 79 | // This array is constructed one of two way depending on the type of the collection. 80 | // 81 | // When type is MetaCollectionType.WithRepetition, then all N! permutations are returned 82 | // and the lexicographic orders are simply generated as 1, 2, ... N. 83 | // E.g. 84 | // Input array: {A A B C D E E} 85 | // Lexicographic Orders: {1 2 3 4 5 6 7} 86 | // 87 | // When type is MetaCollectionType.WithoutRepetition, then fewer are generated, with each 88 | // identical element in the input array not repeated. The lexicographic sort algorithm 89 | // handles this natively as long as the repetition is repeated. 90 | // E.g. 91 | // Input array: {A A B C D E E} 92 | // Lexicographic Orders: {1 1 2 3 4 5 5} 93 | 94 | Type = type; 95 | _myValues = values.ToList(); 96 | _myLexicographicOrders = new int[_myValues.Count]; 97 | 98 | if (type == GenerateOption.WithRepetition) 99 | { 100 | for (var i = 0; i < _myLexicographicOrders.Length; ++i) 101 | { 102 | _myLexicographicOrders[i] = i; 103 | } 104 | } 105 | else 106 | { 107 | comparer ??= Comparer.Default; 108 | 109 | _myValues.Sort(comparer); 110 | var j = 1; 111 | if (_myLexicographicOrders.Length > 0) 112 | { 113 | _myLexicographicOrders[0] = j; 114 | } 115 | 116 | for (var i = 1; i < _myLexicographicOrders.Length; ++i) 117 | { 118 | if (comparer.Compare(_myValues[i - 1], _myValues[i]) != 0) 119 | { 120 | ++j; 121 | } 122 | 123 | _myLexicographicOrders[i] = j; 124 | } 125 | } 126 | 127 | Count = GetCount(); 128 | } 129 | 130 | /// 131 | /// Gets an enumerator for collecting the list of permutations. 132 | /// 133 | /// The enumerator. 134 | public IEnumerator> GetEnumerator() => new Enumerator(this); 135 | 136 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 137 | 138 | /// 139 | /// The enumerator that enumerates each meta-collection of the enclosing Permutations class. 140 | /// 141 | public sealed class Enumerator : IEnumerator> 142 | { 143 | /// 144 | /// Construct a enumerator with the parent object. 145 | /// 146 | /// The source Permutations object. 147 | public Enumerator(Permutations source) 148 | { 149 | _ = source ?? throw new ArgumentNullException(nameof(source)); 150 | _myParent = source; 151 | _myLexicographicalOrders = new int[source._myLexicographicOrders.Length]; 152 | _myValues = new List(source._myValues.Count); 153 | source._myLexicographicOrders.CopyTo(_myLexicographicalOrders, 0); 154 | _myPosition = Position.BeforeFirst; 155 | } 156 | 157 | void IEnumerator.Reset() => throw new NotSupportedException(); 158 | 159 | /// 160 | /// Advances to the next permutation. 161 | /// 162 | /// True if successfully moved to next permutation, False if no more permutations exist. 163 | /// 164 | /// Continuation was tried (i.e. yield return) by was not nearly as efficient. 165 | /// Performance is further increased by using value types and removing generics, that is, the LexicographicOrder parellel array. 166 | /// This is a issue with the .NET CLR not optimizing as well as it could in this infrequently used scenario. 167 | /// 168 | public bool MoveNext() 169 | { 170 | switch (_myPosition) 171 | { 172 | case Position.BeforeFirst: 173 | _myValues.AddRange(_myParent._myValues); 174 | _myPosition = Position.InSet; 175 | break; 176 | case Position.InSet: 177 | if (_myValues.Count < 2) 178 | { 179 | _myPosition = Position.AfterLast; 180 | } 181 | else if (!NextPermutation()) 182 | { 183 | _myPosition = Position.AfterLast; 184 | } 185 | break; 186 | case Position.AfterLast: 187 | break; 188 | default: 189 | throw new ArgumentOutOfRangeException(); 190 | } 191 | return _myPosition != Position.AfterLast; 192 | } 193 | 194 | object IEnumerator.Current => Current; 195 | 196 | /// 197 | /// The current permutation. 198 | /// 199 | public IReadOnlyList Current 200 | { 201 | get 202 | { 203 | if (_myPosition == Position.InSet) 204 | return new List(_myValues); 205 | 206 | throw new InvalidOperationException(); 207 | } 208 | } 209 | 210 | /// 211 | public void Dispose() 212 | { 213 | } 214 | 215 | /// 216 | /// Calculates the next lexicographical permutation of the set. 217 | /// This is a permutation with repetition where values that compare as equal will not 218 | /// swap positions to create a new permutation. 219 | /// http://www.cut-the-knot.org/do_you_know/AllPerm.shtml 220 | /// E. W. Dijkstra, A Discipline of Programming, Prentice-Hall, 1997 221 | /// 222 | /// True if a new permutation has been returned, false if not. 223 | /// 224 | /// This uses the integers of the lexicographical order of the values so that any 225 | /// comparison of values are only performed during initialization. 226 | /// 227 | private bool NextPermutation() 228 | { 229 | var i = _myLexicographicalOrders.Length - 1; 230 | 231 | while (_myLexicographicalOrders[i - 1] >= _myLexicographicalOrders[i]) 232 | { 233 | --i; 234 | if (i == 0) 235 | { 236 | return false; 237 | } 238 | } 239 | 240 | var j = _myLexicographicalOrders.Length; 241 | 242 | while (_myLexicographicalOrders[j - 1] <= _myLexicographicalOrders[i - 1]) 243 | { 244 | --j; 245 | } 246 | 247 | Swap(i - 1, j - 1); 248 | 249 | ++i; 250 | 251 | j = _myLexicographicalOrders.Length; 252 | 253 | while (i < j) 254 | { 255 | Swap(i - 1, j - 1); 256 | ++i; 257 | --j; 258 | } 259 | return true; 260 | } 261 | 262 | /// 263 | /// Helper function for swapping two elements within the internal collection. 264 | /// This swaps both the lexicographical order and the values, maintaining the parallel array. 265 | /// 266 | private void Swap(int i, int j) 267 | { 268 | var temp = _myValues[i]; 269 | _myValues[i] = _myValues[j]; 270 | _myValues[j] = temp; 271 | _myKviTemp = _myLexicographicalOrders[i]; 272 | _myLexicographicalOrders[i] = _myLexicographicalOrders[j]; 273 | _myLexicographicalOrders[j] = _myKviTemp; 274 | } 275 | 276 | /// 277 | /// Single instance of swap variable for int, small performance improvement over declaring in Swap function scope. 278 | /// 279 | private int _myKviTemp; 280 | 281 | /// 282 | /// Flag indicating the position of the enumerator. 283 | /// 284 | private Position _myPosition = Position.BeforeFirst; 285 | 286 | /// 287 | /// Parallel array of integers that represent the location of items in the myValues array. 288 | /// This is generated at Initialization and is used as a performance speed up rather that 289 | /// comparing T each time, much faster to let the CLR optimize around integers. 290 | /// 291 | private readonly int[] _myLexicographicalOrders; 292 | 293 | /// 294 | /// The list of values that are current to the enumerator. 295 | /// 296 | private readonly List _myValues; 297 | 298 | /// 299 | /// The set of permutations that this enumerator enumerates. 300 | /// 301 | private readonly Permutations _myParent; 302 | 303 | /// 304 | /// Internal position type for tracking enumerator position. 305 | /// 306 | private enum Position 307 | { 308 | BeforeFirst, 309 | InSet, 310 | AfterLast 311 | } 312 | } 313 | 314 | /// 315 | /// The count of all permutations that will be returned. 316 | /// If is , then this does not count equivalent result sets. 317 | /// I.e., count of permutations of "AAB" will be 3 instead of 6. 318 | /// If is , then this is all combinations and is therefore N!, where N is the number of values in the input set. 319 | /// 320 | public BigInteger Count { get; } 321 | 322 | /// 323 | /// The type of permutations set that is generated. 324 | /// 325 | public GenerateOption Type { get; } 326 | 327 | /// 328 | /// The upper index of the meta-collection, equal to the number of items in the input set. 329 | /// 330 | public int UpperIndex => _myValues.Count; 331 | 332 | /// 333 | /// The lower index of the meta-collection, equal to the number of items returned each iteration. 334 | /// This is always equal to . 335 | /// 336 | public int LowerIndex => _myValues.Count; 337 | 338 | /// 339 | /// Calculates the total number of permutations that will be returned. 340 | /// As this can grow very large, extra effort is taken to avoid overflowing the accumulator. 341 | /// While the algorithm looks complex, it really is just collecting numerator and denominator terms 342 | /// and cancelling out all of the denominator terms before taking the product of the numerator terms. 343 | /// 344 | /// The number of permutations. 345 | private BigInteger GetCount() 346 | { 347 | var runCount = 1; 348 | var divisors = Enumerable.Empty(); 349 | var numerators = Enumerable.Empty(); 350 | 351 | for (var i = 1; i < _myLexicographicOrders.Length; ++i) 352 | { 353 | numerators = numerators.Concat(SmallPrimeUtility.Factor(i + 1)); 354 | 355 | if (_myLexicographicOrders[i] == _myLexicographicOrders[i - 1]) 356 | { 357 | ++runCount; 358 | } 359 | else 360 | { 361 | for (var f = 2; f <= runCount; ++f) 362 | divisors = divisors.Concat(SmallPrimeUtility.Factor(f)); 363 | 364 | runCount = 1; 365 | } 366 | } 367 | 368 | for (var f = 2; f <= runCount; ++f) 369 | divisors = divisors.Concat(SmallPrimeUtility.Factor(f)); 370 | 371 | return SmallPrimeUtility.EvaluatePrimeFactors( 372 | SmallPrimeUtility.DividePrimeFactors(numerators, divisors) 373 | ); 374 | } 375 | 376 | /// 377 | /// A list of T that represents the order of elements as originally provided. 378 | /// 379 | private readonly List _myValues; 380 | 381 | /// 382 | /// Parallel array of integers that represent the location of items in the myValues array. 383 | /// This is generated at Initialization and is used as a performance speed up rather that 384 | /// comparing T each time, much faster to let the CLR optimize around integers. 385 | /// 386 | private readonly int[] _myLexicographicOrders; 387 | } 388 | } 389 | -------------------------------------------------------------------------------- /src/Combinatorics/SmallPrimeUtility.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Numerics; 6 | 7 | namespace Combinatorics.Collections 8 | { 9 | /// 10 | /// Utility class that maintains a small table of prime numbers and provides 11 | /// simple implementations of Prime Factorization algorithms. 12 | /// This is a quick and dirty utility class to support calculations of permutation 13 | /// sets with indexes under 2^31. 14 | /// The prime table contains all primes up to Sqrt(2^31) which are all of the primes 15 | /// requires to factorize any Int32 positive integer. 16 | /// 17 | internal static class SmallPrimeUtility 18 | { 19 | /// 20 | /// Performs a prime factorization of a given integer using the table of primes in PrimeTable. 21 | /// Since this will only factor Int32 sized integers, a simple list of factors is returned instead 22 | /// of the more scalable, but more difficult to consume, list of primes and associated exponents. 23 | /// 24 | /// The number to factorize, must be positive. 25 | /// A simple list of factors. 26 | public static List Factor(int i) 27 | { 28 | var primeIndex = 0; 29 | var prime = PrimeTable[primeIndex]; 30 | var factors = new List(); 31 | 32 | while (i > 1) 33 | { 34 | #if NETSTANDARD1_1 35 | var remainder = i % prime; 36 | var divResult = remainder == 0 ? i / prime : 0; 37 | #else 38 | var divResult = Math.DivRem(i, prime, out var remainder); 39 | #endif 40 | 41 | if (remainder == 0) 42 | { 43 | factors.Add(prime); 44 | i = divResult; 45 | } 46 | else 47 | { 48 | ++primeIndex; 49 | prime = PrimeTable[primeIndex]; 50 | } 51 | } 52 | 53 | return factors; 54 | } 55 | 56 | /// 57 | /// Given two integers expressed as a list of prime factors, divides these numbers 58 | /// and returns an integer also expressed as a set of prime factors. 59 | /// If the result is not a integer, then the result is undefined. That is, 11 / 5 60 | /// when divided by this function will not yield a correct result. 61 | /// As such, this function is ONLY useful for division with combinatorial results where 62 | /// the result is known to be an integer AND the division occurs as the last operation(s). 63 | /// 64 | /// Numerator argument, expressed as list of prime factors. 65 | /// Denominator argument, expressed as list of prime factors. 66 | /// Resultant, expressed as list of prime factors. 67 | public static List DividePrimeFactors(IEnumerable numerator, IEnumerable denominator) 68 | { 69 | _ = numerator ?? throw new ArgumentNullException(nameof(numerator)); 70 | _ = denominator ?? throw new ArgumentNullException(nameof(denominator)); 71 | var product = numerator.ToList(); 72 | foreach (var prime in denominator) 73 | product.Remove(prime); 74 | return product; 75 | } 76 | 77 | /// 78 | /// Given a list of prime factors returns the long representation. 79 | /// 80 | /// Integer, expressed as list of prime factors. 81 | /// Standard long representation. 82 | public static BigInteger EvaluatePrimeFactors(IEnumerable value) 83 | { 84 | _ = value ?? throw new ArgumentNullException(nameof(value)); 85 | BigInteger result = 1; 86 | foreach (var prime in value) 87 | result *= prime; 88 | return result; 89 | } 90 | 91 | /// 92 | /// Static initializer, set up prime table. 93 | /// 94 | static SmallPrimeUtility() 95 | { 96 | PrimeTable = CalculatePrimes(); 97 | } 98 | 99 | /// 100 | /// Calculate all primes up to Sqrt(2^32) = 2^16. 101 | /// This table will be large enough for all factorizations for Int32's. 102 | /// Small tables are best built using the Sieve Of Eratosthenes, 103 | /// Reference: http://primes.utm.edu/glossary/page.php?sort=SieveOfEratosthenes 104 | /// 105 | private static IReadOnlyList CalculatePrimes() 106 | { 107 | // Build Sieve Of Eratosthenes 108 | var sieve = new BitArray(65536, true); 109 | for (var possiblePrime = 2; possiblePrime <= 256; ++possiblePrime) 110 | { 111 | if (!sieve[possiblePrime]) continue; 112 | 113 | // It is prime, so remove all future factors... 114 | for (var nonPrime = 2 * possiblePrime; nonPrime < 65536; nonPrime += possiblePrime) 115 | sieve[nonPrime] = false; 116 | } 117 | 118 | // Scan sieve for primes... 119 | var primes = new List(); 120 | for (var i = 2; i < 65536; ++i) 121 | { 122 | if (sieve[i]) 123 | primes.Add(i); 124 | } 125 | 126 | return primes; 127 | } 128 | 129 | /// 130 | /// A List of all primes from 2 to 2^16. 131 | /// 132 | public static IReadOnlyList PrimeTable { get; } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/Combinatorics/Variations.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Numerics; 6 | 7 | namespace Combinatorics.Collections 8 | { 9 | /// 10 | /// Variations defines a sequence of all possible ordered subsets of a particular size from the set of values. 11 | /// 12 | /// 13 | /// The MetaCollectionType parameter of the constructor allows for the creation of 14 | /// normal Variations and Variations with Repetition. 15 | /// 16 | /// When given an input collect {A B C} and lower index of 2, the following sets are generated: 17 | /// MetaCollectionType.WithoutRepetition generates 6 sets: => 18 | /// {A B}, {A B}, {B A}, {B C}, {C A}, {C B} 19 | /// MetaCollectionType.WithRepetition generates 9 sets: 20 | /// {A A}, {A B}, {A B}, {B A}, {B B }, {B C}, {C A}, {C B}, {C C} 21 | /// 22 | /// The equality of multiple inputs is not considered when generating variations. 23 | /// 24 | /// The type of the values within the list. 25 | public sealed class Variations : IEnumerable> 26 | { 27 | /// 28 | /// Create a variation set from the indicated list of values. 29 | /// The upper index is calculated as values.Count, the lower index is specified. 30 | /// Collection type defaults to MetaCollectionType.WithoutRepetition 31 | /// 32 | /// List of values to select Variations from. 33 | /// The size of each variation set to return. 34 | public Variations(IEnumerable values, int lowerIndex) 35 | : this(values, lowerIndex, GenerateOption.WithoutRepetition) 36 | { 37 | } 38 | 39 | /// 40 | /// Create a variation set from the indicated list of values. 41 | /// The upper index is calculated as values.Count, the lower index is specified. 42 | /// 43 | /// List of values to select variations from. 44 | /// The size of each variation set to return. 45 | /// Type indicates whether to use repetition in set generation. 46 | public Variations(IEnumerable values, int lowerIndex, GenerateOption type) 47 | { 48 | Type = type; 49 | LowerIndex = lowerIndex; 50 | _myValues = values.ToList(); 51 | 52 | if (type != GenerateOption.WithoutRepetition) 53 | { 54 | return; 55 | } 56 | 57 | var myMap = new List(_myValues.Count); 58 | var index = 0; 59 | for (var i = 0; i < _myValues.Count; ++i) 60 | myMap.Add(i >= _myValues.Count - LowerIndex ? index++ : int.MaxValue); 61 | 62 | _myPermutations = new Permutations(myMap); 63 | } 64 | 65 | /// 66 | /// Gets an enumerator for the collection of Variations. 67 | /// 68 | /// The enumerator. 69 | public IEnumerator> GetEnumerator() => 70 | Type == GenerateOption.WithRepetition ? 71 | (IEnumerator>) new EnumeratorWithRepetition(this) : 72 | new EnumeratorWithoutRepetition(this); 73 | 74 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 75 | 76 | /// 77 | /// An enumerator for Variations when the type is set to WithRepetition. 78 | /// 79 | public sealed class EnumeratorWithRepetition : IEnumerator> 80 | { 81 | /// 82 | /// Construct a enumerator with the parent object. 83 | /// 84 | /// The source Variations object. 85 | public EnumeratorWithRepetition(Variations source) 86 | { 87 | _myParent = source; 88 | _myCurrentList = null; 89 | _myListIndexes = null; 90 | } 91 | 92 | void IEnumerator.Reset() => throw new NotSupportedException(); 93 | 94 | /// 95 | /// Advances to the next variation. 96 | /// 97 | /// True if successfully moved to next variation, False if no more variations exist. 98 | /// 99 | /// Increments the internal myListIndexes collection by incrementing the last index 100 | /// and overflow/carrying into others just like grade-school arithmetic. If the 101 | /// final carry flag is set, then we would wrap around and are therefore done. 102 | /// 103 | public bool MoveNext() 104 | { 105 | var carry = 1; 106 | if (_myListIndexes == null) 107 | { 108 | _myListIndexes = new List(_myParent.LowerIndex); 109 | for (var i = 0; i < _myParent.LowerIndex; ++i) 110 | { 111 | _myListIndexes.Add(0); 112 | } 113 | carry = 0; 114 | } 115 | else 116 | { 117 | for (var i = _myListIndexes.Count - 1; i >= 0 && carry > 0; --i) 118 | { 119 | _myListIndexes[i] += carry; 120 | carry = 0; 121 | 122 | if (_myListIndexes[i] < _myParent.UpperIndex) 123 | { 124 | continue; 125 | } 126 | 127 | _myListIndexes[i] = 0; 128 | carry = 1; 129 | } 130 | } 131 | _myCurrentList = null; 132 | return carry != 1; 133 | } 134 | 135 | /// 136 | /// The current variation 137 | /// 138 | public IReadOnlyList Current 139 | { 140 | get 141 | { 142 | ComputeCurrent(); 143 | return _myCurrentList!; 144 | } 145 | } 146 | 147 | object IEnumerator.Current => Current; 148 | 149 | /// 150 | public void Dispose() 151 | { 152 | } 153 | 154 | /// 155 | /// Computes the current list based on the internal list index. 156 | /// 157 | private void ComputeCurrent() 158 | { 159 | if (_myCurrentList != null) 160 | { 161 | return; 162 | } 163 | 164 | _ = _myListIndexes ?? throw new InvalidOperationException($"Cannot call {nameof(Current)} before calling {nameof(MoveNext)}."); 165 | 166 | _myCurrentList = new List(_myListIndexes.Count); 167 | foreach (var index in _myListIndexes) 168 | _myCurrentList.Add(_myParent._myValues[index]); 169 | } 170 | 171 | /// 172 | /// Parent object this is an enumerator for. 173 | /// 174 | private readonly Variations _myParent; 175 | 176 | /// 177 | /// The current list of values, this is lazy evaluated by the Current property. 178 | /// 179 | private List? _myCurrentList; 180 | 181 | /// 182 | /// An enumerator of the parents list of lexicographic orderings. 183 | /// 184 | private List? _myListIndexes; 185 | } 186 | 187 | /// 188 | /// An enumerator for Variations when the type is set to WithoutRepetition. 189 | /// 190 | public sealed class EnumeratorWithoutRepetition : IEnumerator> 191 | { 192 | /// 193 | /// Construct a enumerator with the parent object. 194 | /// 195 | /// The source Variations object. 196 | public EnumeratorWithoutRepetition(Variations source) 197 | { 198 | _myParent = source; 199 | _myPermutationsEnumerator = (Permutations.Enumerator)_myParent._myPermutations!.GetEnumerator(); 200 | } 201 | 202 | void IEnumerator.Reset() => throw new NotSupportedException(); 203 | 204 | /// 205 | /// Advances to the next variation. 206 | /// 207 | /// True if successfully moved to next variation, False if no more variations exist. 208 | public bool MoveNext() 209 | { 210 | var ret = _myPermutationsEnumerator.MoveNext(); 211 | _myCurrentList = null; 212 | return ret; 213 | } 214 | 215 | /// 216 | /// The current variation. 217 | /// 218 | public IReadOnlyList Current 219 | { 220 | get 221 | { 222 | ComputeCurrent(); 223 | return _myCurrentList!; 224 | } 225 | } 226 | 227 | object IEnumerator.Current => Current; 228 | 229 | /// 230 | public void Dispose() => _myPermutationsEnumerator.Dispose(); 231 | 232 | /// 233 | /// Creates a list of original values from the int permutation provided. 234 | /// The exception for accessing current (InvalidOperationException) is generated 235 | /// by the call to .Current on the underlying enumeration. 236 | /// 237 | /// 238 | /// To compute the current list of values, the element to use is determined by 239 | /// a permutation position with a non-MaxValue value. It is placed at the position in the 240 | /// output that the index value indicates. 241 | /// 242 | /// E.g. Variations of 6 choose 3 without repetition 243 | /// Input array: {A B C D E F} 244 | /// Permutations: {- 1 - - 3 2} (- is Int32.MaxValue) 245 | /// Generates set: {B F E} 246 | /// 247 | private void ComputeCurrent() 248 | { 249 | if (_myCurrentList != null) 250 | { 251 | return; 252 | } 253 | 254 | _myCurrentList = new List(_myParent.LowerIndex); 255 | var index = 0; 256 | var currentPermutation = _myPermutationsEnumerator.Current; 257 | 258 | for (var i = 0; i < _myParent.LowerIndex; ++i) 259 | { 260 | _myCurrentList.Add(_myParent._myValues[0]); 261 | } 262 | 263 | foreach (var position in currentPermutation) 264 | { 265 | if (position != int.MaxValue) 266 | { 267 | _myCurrentList[position] = _myParent._myValues[index]; 268 | if (_myParent.Type == GenerateOption.WithoutRepetition) 269 | { 270 | ++index; 271 | } 272 | } 273 | else 274 | { 275 | ++index; 276 | } 277 | } 278 | } 279 | 280 | /// 281 | /// Parent object this is an enumerator for. 282 | /// 283 | private readonly Variations _myParent; 284 | 285 | /// 286 | /// The current list of values, this is lazy evaluated by the Current property. 287 | /// 288 | private List? _myCurrentList; 289 | 290 | /// 291 | /// An enumerator of the parents list of lexicographic orderings. 292 | /// 293 | private readonly Permutations.Enumerator _myPermutationsEnumerator; 294 | } 295 | 296 | /// 297 | /// The number of unique variations that are defined in this meta-collection. 298 | /// 299 | /// 300 | /// Variations with repetitions does not behave like other meta-collections and it's 301 | /// count is equal to N^P, where N is the upper index and P is the lower index. 302 | /// 303 | public BigInteger Count => Type == GenerateOption.WithoutRepetition ? _myPermutations!.Count : BigInteger.Pow(UpperIndex, LowerIndex); 304 | 305 | /// 306 | /// The type of Variations set that is generated. 307 | /// 308 | public GenerateOption Type { get; } 309 | 310 | /// 311 | /// The upper index of the meta-collection, equal to the number of items in the initial set. 312 | /// 313 | public int UpperIndex => _myValues.Count; 314 | 315 | /// 316 | /// The lower index of the meta-collection, equal to the number of items returned each iteration. 317 | /// 318 | public int LowerIndex { get; } 319 | 320 | /// 321 | /// Copy of values object is initialized with, required for enumerator reset. 322 | /// 323 | private readonly List _myValues; 324 | 325 | /// 326 | /// Permutations object that handles permutations on int for variation inclusion and ordering. 327 | /// 328 | private readonly Permutations? _myPermutations; 329 | } 330 | } 331 | -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | true 16 | latest 17 | enable 18 | true 19 | 20 | 21 | true 22 | 23 | 24 | 25 | all 26 | runtime; build; native; contentfiles; analyzers; buildtransitive 27 | 28 | 29 | 30 | 31 | 32 | https://github.com/$(GITHUB_REPOSITORY) 33 | MIT 34 | 35 | 36 | 37 | 38 | icon.png 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | true 47 | true 48 | true 49 | snupkg 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | dev 63 | 64 | 65 | 66 | 67 | 68 | 69 | 73 | 74 | <_LocalTopLevelSourceRoot Include="@(SourceRoot)" Condition="'%(SourceRoot.NestedRoot)' == ''"/> 75 | 76 | 77 | -------------------------------------------------------------------------------- /src/Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | false 11 | false 12 | NU5128 13 | 14 | 15 | 16 | true 17 | 18 | -------------------------------------------------------------------------------- /src/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eoincampbell/combinatorics/53cfaf93c67e7014adc060e6d271bb95a1b488e8/src/icon.png -------------------------------------------------------------------------------- /src/project.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | Adrian Akison;Eoin Campbell;Stephen Cleary 4 | 2.0.0 5 | 6 | -------------------------------------------------------------------------------- /test/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | latest 9 | 10 | 11 | 12 | 13 | 14 | 15 | all 16 | runtime; build; native; contentfiles; analyzers; buildtransitive 17 | 18 | 19 | all 20 | runtime; build; native; contentfiles; analyzers; buildtransitive 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /test/UnitTests/CombinatoricTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Combinatorics.Collections; 3 | using Xunit; 4 | 5 | namespace UnitTests 6 | { 7 | /// 8 | /// Tests Cases & Examples for Combinations, Permutations & Variations with & without repetition in the output sets. 9 | /// 10 | public class CombinatoricTests 11 | { 12 | /// 13 | /// Standard permutations simply provide every single ordering of the input set. 14 | /// Permutations of {A B C}: {A B C}, {A C B}, {B A C}, {B C A}, {C A B}, {C B A} 15 | /// The number of Permutations can be easily shown to be P(n) = n!, where n is the number of items. 16 | /// In the above example, the input set contains 3 items, and the size is 3! = 6. 17 | /// This means that the number of permutations grows exponentially with n. 18 | /// Even a small n can create massive numbers of Permutations; for example, the number of ways to randomly 19 | /// shuffle a deck of cards is 52! or approximately 8.1E67. 20 | /// 21 | [Fact] 22 | public void Generate_Permutations_Without_Repetition_On_3_Unique_Input_Items_Should_Create_6_Output_Permutations() 23 | { 24 | var integers = new List { 1, 2, 3 }; 25 | 26 | var p = new Permutations(integers, GenerateOption.WithoutRepetition); 27 | 28 | foreach (var v in p) 29 | { 30 | System.Diagnostics.Debug.WriteLine(string.Join(",", v)); 31 | } 32 | 33 | Assert.Equal(6, p.Count); 34 | } 35 | 36 | /// 37 | /// Permutations with Repetition sets give allowance for repetitive items in the input 38 | /// set that reduce the number of permutations: 39 | /// Permutations with Repetition of the set {A A B}: {A A B}, {A B A}, {B A A} 40 | /// The number of Permutations with Repetition is not as large, being reduced by the number and count 41 | /// of repetitive items in the input set. For each set of m identical items, the overall count is reduced by m!. 42 | /// In the above example, the input set contains 3 items with one subset of 2 identical items, 43 | /// the count is 3! / 2! = 6 / 2 = 3. The idea behind the count is easier than the formula since the formula 44 | /// requires the product of each repetitive set of size ri. The total size is Pr(n) = n! / Π(ri!) 45 | /// (where Π is the product operator). All of the collating and calculating is handled for us using 46 | /// the Permutation.Count property. 47 | /// 48 | [Fact] 49 | public void Generate_Permutations_With_Repetition_On_4_Input_Items_Including_Duplicates_Should_Create_24_Output_Permutations() 50 | { 51 | var integers = new List {1, 1, 2, 3}; 52 | 53 | var p = new Permutations(integers, GenerateOption.WithRepetition); 54 | 55 | foreach (var v in p) 56 | { 57 | System.Diagnostics.Debug.WriteLine(string.Join(",", v)); 58 | } 59 | 60 | Assert.Equal(24, p.Count); 61 | } 62 | 63 | /// 64 | /// Combinations are subsets of a given size taken from a given input set. 65 | /// The size of the set is known as the Upper Index (n) and the size of the subset is known as the Lower Index (k). 66 | /// When counting the number of combinations, the terminology is generally "n choose k", 67 | /// and is known as the Binomial Coefficient [3]. Unlike permutations, combinations do not have any order 68 | /// in the output set. Combinations without Repetition are would be similar to drawing balls from a lottery drum. 69 | /// Each ball can only be drawn once but the order they are drawn in is unimportant. 70 | /// 71 | [Fact] 72 | public void Generate_Combinations_of_3_Without_Repetition_On_6_Input_Items_Should_Create_20_Output_Items() 73 | { 74 | var integers = new List {1, 2, 3, 4, 5, 6}; 75 | 76 | var c = new Combinations(integers, 3, GenerateOption.WithoutRepetition); 77 | 78 | foreach (var v in c) 79 | { 80 | System.Diagnostics.Debug.WriteLine(string.Join(",", v)); 81 | } 82 | 83 | Assert.Equal(20, c.Count); 84 | } 85 | 86 | /// 87 | /// Combinations with Repetition are determined by looking at a set of items, 88 | /// and selecting a subset while allowing repetition. For example, roll a dice, write down the letter, 89 | /// and roll the dice again. The previous result does not preclude you from getting the same result again. 90 | /// The order is still unimportant 91 | /// 92 | [Fact] 93 | public void Generate_Combinations_of_2_With_Repetition_On_6_Input_Items_Should_Create_21_Output_Items() 94 | { 95 | var integers = new List { 1, 2, 3, 4, 5, 6 }; 96 | 97 | var c = new Combinations(integers, 2, GenerateOption.WithRepetition); 98 | 99 | foreach (var v in c) 100 | { 101 | System.Diagnostics.Debug.WriteLine(string.Join(",", v)); 102 | } 103 | 104 | Assert.Equal(21, c.Count); 105 | } 106 | 107 | /// 108 | /// Variations combine features of combinations and permutations, they are the set of all ordered 109 | /// combinations of items to make up a subset. Like combinations, the size of the set is known as 110 | /// the Upper Index (n) and the size of the subset is known as the Lower Index (k). And, the 111 | /// generation of variations can be based on the repeating of output items. These are called Variations. 112 | /// 113 | /// Variations are permutations of combinations. That is, a variation of a set of n items choose k, 114 | /// is the ordered subsets of size k. For example: 115 | /// Variations of {A B C} choose 2: {A B}, {A C}, {B A}, {B C}, {C A}, {C B} 116 | /// The number of outputs in this particular example is similar to the number of combinations of 117 | /// n choose k divided by the permutations of k. 118 | /// 119 | /// It can be calculated as V(n, k) = C(n, k) * P(k) = (n! / ( k! * (n - k)! )) * k! = n! / (n - k)!. 120 | /// 121 | [Fact] 122 | public void Generate_Variations_of_3_Without_Repetition_On_6_Input_Items_Should_Create_120_Output_Items() 123 | { 124 | var integers = new List {1, 2, 3, 4, 5, 6}; 125 | 126 | var v = new Variations(integers, 3, GenerateOption.WithoutRepetition); 127 | 128 | foreach (var vv in v) 129 | { 130 | System.Diagnostics.Debug.WriteLine(string.Join(",", vv)); 131 | } 132 | 133 | Assert.Equal(120, v.Count); 134 | } 135 | 136 | /// 137 | /// Variations with Repetition expands on the set of variations, and allows items to be reused. 138 | /// Since each item can be re-used, this allows for variations to include all items in the output to be a 139 | /// single item from the input. For example: 140 | /// Variations with Repetition of {A B C} choose 2: {A A}, {A B}, {A C}, {B A}, {B B}, {B C}, {C A}, {C B}, {C C} 141 | /// The size of the output set for variations is easier to compute since factorials are not involved. 142 | /// Each of the p positions can be filled from any of the n positions in the input set. 143 | /// The first item is one of n items, the second is also one of n, and the pth is also one of n. 144 | /// This gives us Vr(n, k) = n^k total variations of n items choose k. 145 | /// 146 | [Fact] 147 | public void Generate_Variations_of_3_With_Repetition_On_6_Input_Items_Should_Create_216_Output_Items() 148 | { 149 | var integers = new List { 1, 2, 3, 4, 5, 6 }; 150 | 151 | var v = new Variations(integers, 3, GenerateOption.WithRepetition); 152 | 153 | foreach (var vv in v) 154 | { 155 | System.Diagnostics.Debug.WriteLine(string.Join(",", vv)); 156 | } 157 | 158 | Assert.Equal(216, v.Count); 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /test/UnitTests/UnitTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | --------------------------------------------------------------------------------