├── .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 | [](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 |
--------------------------------------------------------------------------------