-
-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC 0005: Combinatoric library #5
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,142 @@ | ||||||||||||||||||||||
- Feature Name: `math-combinatoric-library` | ||||||||||||||||||||||
- Start Date: 2024-02-09 | ||||||||||||||||||||||
- RFC PR: [crystal-lang/rfcs#0005](https://github.com/crystal-lang/rfcs/pull/0005) | ||||||||||||||||||||||
- Issue: - | ||||||||||||||||||||||
|
||||||||||||||||||||||
# Summary | ||||||||||||||||||||||
|
||||||||||||||||||||||
Add combinatoric and permutations methods to the standard library which works on integers. | ||||||||||||||||||||||
|
||||||||||||||||||||||
# Motivation | ||||||||||||||||||||||
|
||||||||||||||||||||||
Crystal has an already exsisting methods for doing combinations and permutations but those work on some form of collection which for larger sizes makes them very computanional heavy and high memory usage. | ||||||||||||||||||||||
In the scientific world is there many uses of getting to know the size of combinations or permutations but not the individual combinations or permutations. | ||||||||||||||||||||||
These methods could also have an `Int` implementation which doesnt use any form of collections to reduce on the calculations and memory usage required. | ||||||||||||||||||||||
There are already exsisting libraries which impliments this logic but most of them are unmaintained. | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you add a few references to those libraries? Do some of those already have an optimized implementation? |
||||||||||||||||||||||
|
||||||||||||||||||||||
The expected outcome out of this is that Crystal will have a stronger standard library for working with scientific purposes. | ||||||||||||||||||||||
But also when working with just sizes of permutations and combinations will have drasticly faster executions. | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One particularly important use case is preallocating the array used for # note: this should not require `BigInt` to work!
ary = Array(Array(Int32)).new(6.combinations(3) + 1)
[0, 1, 4, 9, 16, 25].each_combination(3) { |v| arr << v }
arr << 0 # okay, no reallocation Whether |
||||||||||||||||||||||
|
||||||||||||||||||||||
# Guide-level explanation | ||||||||||||||||||||||
|
||||||||||||||||||||||
When you just want the size of a combination or permutations so are the `BigInt#combination` or `BigInt#permutation` methods way more efficent when working on arrays. | ||||||||||||||||||||||
They work using a mathematical formula based on factorial of numbers. | ||||||||||||||||||||||
|
||||||||||||||||||||||
To use the methods will you have to include the `BigInt` libaries using `require "big"`. | ||||||||||||||||||||||
Deffine a number then you can use the method on that number with how many combinations you want to use. | ||||||||||||||||||||||
|
||||||||||||||||||||||
```crystal | ||||||||||||||||||||||
require "big" | ||||||||||||||||||||||
|
||||||||||||||||||||||
number : BigInt = 5 | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These |
||||||||||||||||||||||
number.combinations(3) | ||||||||||||||||||||||
# => 10 | ||||||||||||||||||||||
|
||||||||||||||||||||||
number.combinations(2) | ||||||||||||||||||||||
# => 10 | ||||||||||||||||||||||
``` | ||||||||||||||||||||||
|
||||||||||||||||||||||
In the examples above can the number be reasoned about having 5 items and wanting to know how many ways those 5 items can be put in groups of 3 or 2. | ||||||||||||||||||||||
The reason why those 2 examples becomes the same can be reasoned out of 2 perspectives: | ||||||||||||||||||||||
|
||||||||||||||||||||||
- Say you would want to add 5 new features to Crystal but you are only allowed to add 3. | ||||||||||||||||||||||
- That would be the same as resoning that you would want to get groups of 2 features that doesnt get added. | ||||||||||||||||||||||
|
||||||||||||||||||||||
Intrested in learning more: https://en.wikipedia.org/wiki/Combination | ||||||||||||||||||||||
|
||||||||||||||||||||||
Another example: | ||||||||||||||||||||||
|
||||||||||||||||||||||
```crystal | ||||||||||||||||||||||
require "big" | ||||||||||||||||||||||
|
||||||||||||||||||||||
number : BigInt = 20 | ||||||||||||||||||||||
number.combinations(2) | ||||||||||||||||||||||
# => 190 | ||||||||||||||||||||||
|
||||||||||||||||||||||
number_2 : BigInt = 10 | ||||||||||||||||||||||
number_2.combinations(2) | ||||||||||||||||||||||
# => 45 | ||||||||||||||||||||||
``` | ||||||||||||||||||||||
|
||||||||||||||||||||||
Error handeling examples: | ||||||||||||||||||||||
|
||||||||||||||||||||||
```crystal | ||||||||||||||||||||||
require "big" | ||||||||||||||||||||||
|
||||||||||||||||||||||
number : BigInt = 5 | ||||||||||||||||||||||
number.combinations(-4) | ||||||||||||||||||||||
# ArgummentError: combinations can't be done on negativ integers | ||||||||||||||||||||||
|
||||||||||||||||||||||
number2 : BigInt = -5 | ||||||||||||||||||||||
number2.combinations(2) | ||||||||||||||||||||||
# ArgummentError: combinations can't be done on negativ integers | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well you are sorta never dealing with negative combinations, and this method in Python and my calculator returns error when trying to use a negative integer. Then also the combination method is in math derived from factorial and in Crystal does the factorial method not accept negative integers. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But you said that these methods contribute to "a stronger standard library for working with scientific purposes", and mathematical science dictates that this generalization is valid, otherwise there wouldn't be a reason GMP explicitly states that negative IMO the restriction to non-negative There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mean, I am perhaps not the best at math, but I find the problem with negative numbers is that most of this works around vectors. Vectors' size can't be negative, but their direction can be. This means if we have the vector : {3, 4} and would multiply it by -1, then it would be {-3, -4}. Its direction is different, but its length or size remains the same, which would be: (-3 ^ 2 + -5 ^2) ^ 0.5 = 5. If we start allowing negative integers, then we start claiming that these vectors' size is negative, which is mathematically impossible without using complex numbers. With this said, I have a hard time seeing how this can be generalized with negative numbers. I barely know anything about GMP, and I am up for changes if it is possible to generalize these methods, but I am not aware of how that can be implemented. |
||||||||||||||||||||||
``` | ||||||||||||||||||||||
|
||||||||||||||||||||||
As for permutations does that work simiralliy with the use `BigInt` libaries using `require "big"`. | ||||||||||||||||||||||
|
||||||||||||||||||||||
```Crystal | ||||||||||||||||||||||
require "big" | ||||||||||||||||||||||
|
||||||||||||||||||||||
number : BigInt = 5 | ||||||||||||||||||||||
number.permutations(4) | ||||||||||||||||||||||
# => 120 | ||||||||||||||||||||||
``` | ||||||||||||||||||||||
|
||||||||||||||||||||||
Combinations and permutations share a lot with each other. | ||||||||||||||||||||||
The key difference between combinations and permutations is that permutations care about the order. | ||||||||||||||||||||||
Take the last example with new features, say instead you have to make a list of the 3 features you would like to see in the language and the first feature is the one you want to see the most to be implemented. | ||||||||||||||||||||||
Then the order matters you put them in the list. | ||||||||||||||||||||||
|
||||||||||||||||||||||
Permutations doesnt have the same pattern as combinations with `5.combinations(3)` is the same as `5.combinations(2)`. | ||||||||||||||||||||||
Instead for permutations so are `n.combinations(n)` always equal `n.combinations(n - 1)` (as long as n is a posstive number and not zero). | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typo?
Suggested change
|
||||||||||||||||||||||
|
||||||||||||||||||||||
```crystal | ||||||||||||||||||||||
require "big" | ||||||||||||||||||||||
|
||||||||||||||||||||||
number : BigInt = 5 | ||||||||||||||||||||||
number.combinations(5) | ||||||||||||||||||||||
# => 120 | ||||||||||||||||||||||
|
||||||||||||||||||||||
number.combinations(4) | ||||||||||||||||||||||
# => 120 | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||
``` | ||||||||||||||||||||||
|
||||||||||||||||||||||
This is because say you have a list of the 4 features in order you most want to see in the languages. | ||||||||||||||||||||||
Adding one more feature to the list would not add another combinations since there is only 1 feature left of choosing. | ||||||||||||||||||||||
|
||||||||||||||||||||||
# Reference-level explanation | ||||||||||||||||||||||
|
||||||||||||||||||||||
Since both of these methods quickly reach large numbers so do they both have to be implemented under `BigInt`. | ||||||||||||||||||||||
They could either be written manualy using the mathematical formula togehter with `BigInt#factorial` or using a more optimzied version. | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. GMP already defines |
||||||||||||||||||||||
The mathematical formula for combinations: C(n,k) = (n!) / (k! * (n - k)!) | ||||||||||||||||||||||
The mathematical formula for permutations: P(n,k) = n! / k! | ||||||||||||||||||||||
The `!` is factorial. | ||||||||||||||||||||||
|
||||||||||||||||||||||
Another alternative is to use: https://www.gnu.org/software/gsl/ | ||||||||||||||||||||||
|
||||||||||||||||||||||
# Drawbacks | ||||||||||||||||||||||
|
||||||||||||||||||||||
Dependent on which apparoch is used so would the mathematical formula mostly add more maintaince burden. | ||||||||||||||||||||||
The gsl library would add another library which has to be activly made sure to keep combatible to and in that way also add maintaince burden. | ||||||||||||||||||||||
|
||||||||||||||||||||||
# Rationale and alternatives | ||||||||||||||||||||||
|
||||||||||||||||||||||
The impact of not doing this would mostly mean that these features remains library exclusive, it could have certain benefits to that since the standard library would likely not be able to store all possible methods. | ||||||||||||||||||||||
Although currently there is no real math library which has everything you have to combine a set of libraries if you would like to get a large amount of mathematical methods in various areas. | ||||||||||||||||||||||
Alternativly is that users implements this logic by themselves which could mean unoptimized methods or repatativ method implementation. | ||||||||||||||||||||||
In worst case a user could implement this using arrays (together with the built-in methods), which for large models would be very slow. | ||||||||||||||||||||||
|
||||||||||||||||||||||
# Prior art | ||||||||||||||||||||||
|
||||||||||||||||||||||
In quite a few languages does these methods comes in form of libraries, there could be many reasons to this including the fact that some languages wants to keep the standard library smal. | ||||||||||||||||||||||
|
||||||||||||||||||||||
Although, python added a `math.comb` and `math.perm` method in 3.8. | ||||||||||||||||||||||
This could be due to the fact being more math "focused" then others, but the methods have been appricated for users which have had to use those types of methods. | ||||||||||||||||||||||
|
||||||||||||||||||||||
# Unresolved questions | ||||||||||||||||||||||
|
||||||||||||||||||||||
Should Crystal lang add these combinatoric methods or should they remain to be gatherd through libraries? | ||||||||||||||||||||||
|
||||||||||||||||||||||
# Future possibilities | ||||||||||||||||||||||
|
||||||||||||||||||||||
I cant think of anything. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If anything, the
Int::Primitive
definitions should come first before theBigInt
one, e.g. using the multiplicative definition. Having the methods available only inBigInt
greatly restricts its utility.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah but combinatoric calculations work on very big numbers. I don't know if a more efficient method can be found. but the one which is defined in math. For example
20.combinations(5)
. Would do20!
, which is already 2.432902e+18. And then divide that number, you could likely optimize it a bit, but it is still big numbers which are being worked on. So aslong as the group is small it is fine but when it passes like 50 for even an optimized version so do it require bigint.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A good start is something like
20 // 1 * 19 // 2 * 18 // 3 * 17 // 4 * 16 // 5
. The divisions are all exact, because the firstn
multiplied values must have exactly one multiple ofn
itself, and this will minimize the possibility of intermediate overflows. It makes sense that50.combinations(2)
would simply do50 * 49 // 2
without ever calling any factorial function.