Skip to content
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

Added Fast Power in Number Theory #76

Merged
merged 11 commits into from
Jul 20, 2017
Merged

Added Fast Power in Number Theory #76

merged 11 commits into from
Jul 20, 2017

Conversation

ahmad307
Copy link
Contributor

@ahmad307 ahmad307 commented Jul 5, 2017

This is my first PR, please tell me if there are any issues to be fixed.
Relates to issue #75.

Copy link
Member

@faheel faheel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be great if you also add tests for this, similar to the ones found here. Let me know if you need more information regarding it.

#include <iostream>

using namespace std;
typedef long long ll;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use unsigned long long for double the range.

typedef long long ll;

//Function that returns x raised to the power of y
ll fastPower (ll x,ll y)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use more meaningful variable names like base and exponent instead of just x and y.
Also, rename the function to fast_power to adhere to the guidelines.

@@ -0,0 +1,47 @@
/*
<Fast Power>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove the angle brackets (< and >) and indent the block by one level (4 spaces).


cin>>base>>power;

cout<<fastPower(base,power);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a trailing "\n" so that the prompt on terminals is displayed in the next line.


Space Complexity
------------------
O(log(N)) where N is the power the number is raised to.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where N is the power the number is raised to
More simply, where N is the exponent.

<Fast Power>
--------------
Fast Power is an optimized algorithm to compute exponentiation in a short time.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be helpful for others if you could also mention in 1 or 2 lines more what the algorithm does.

@ahmad307
Copy link
Contributor Author

ahmad307 commented Jul 6, 2017

Regarding adding tests. Shall I add some sample tests commented?
Or shall I make them this way:

TEST_CASE("Base cases", "[fast_power]") {
   REQUIRE(fast_power(2,2) == 4);
   REQUIRE(fast_power(2,4) == 16);
}

@faheel
Copy link
Member

faheel commented Jul 6, 2017

Like the example you gave.

I have updated the file with the required changes alongside adding sample tests.
//Sample Tests
TEST_CASE("Base cases", "[fast_power]") {
REQUIRE(fast_power(2,2) == 4);
REQUIRE(fast_power(2,4) == 16);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you didn't exactly understand how to add tests, but no worries 😄! Read the second comment in issue #41 to know the exact steps in detail.

@ahmad307
Copy link
Contributor Author

ahmad307 commented Jul 7, 2017

I have now added a test file for fastPower.cpp, and edited fastPower.cpp to be compatible.

Copy link
Member

@faheel faheel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well done on adding the tests! 👍
However there are a few issues I have mentioned in the review. Also, the f in the name of the test file should be capital.


TEST_CASE("Overflow cases", "[fast_power]") {
REQUIRE(fast_power(2,100) == 0);
REQUIRE(fast_power(10,99) == 0);
Copy link
Member

@faheel faheel Jul 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fast_power(2, 100) should be 68719476736 (see this) and fast_power(10, 99) should be 4440381706574496940U (see this). Suffix U is for unsigned.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you meant fast_power(2 ,100) in the first one.
Anyway, regarding large numbers, to fix the output being 0 we can output them modulo a large number such as 1000000000007 , but not a huge number like 2^64. Where fast_power(2, 100) will be 40955801449 .
Regarding the last statement, do you mean changing ull to Ull ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I meant fast_power(2, 100) in the first one. Fixed it.

I don't think you need to output modulo a specific number. The result will automatically be modulo MAX_ULL, which is 264-1.

No, ull is fine. I meant you need to add the suffix U to unsigned numbers greater than 263-1 to prevent warnings.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aha! Sorry for the misunderstanding.

int main()
{
//Testing the function
int base,exponent;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

int ull


TEST_CASE("Base cases", "[fast_power]") {
REQUIRE(fast_power(2,2) == 4);
REQUIRE(fast_power(2,4) == 16);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Under "Base cases", check the values of the following:

  • fast_power(0, 0) is undefined or NaN. Comment this case here and handle it properly in the code.
  • fast_power(0, 1) should be 0
  • fast_power(1, 0) should be 1
  • fast_power(1, 1) should be 1

REQUIRE(fast_power(1,1) == 1);
REQUIRE(fast_power(2,2) == 4);
REQUIRE(fast_power(2,2) == 4);
REQUIRE(fast_power(2,4) == 16);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The last 2 can be added under "Normal cases". Also the (2, 2) case has a duplicate.

@ahmad307
Copy link
Contributor Author

ahmad307 commented Jul 9, 2017

Requested changes are all done. Are there any other issues to be fixed?


TEST_CASE("Overflow cases", "[fast_power]") {
REQUIRE(fast_power(2,100) == 68719476736);
REQUIRE(fast_power(10,99) == 4440381706574496940U);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both these tests are failing, and I think the reason is that at some step of the calculation, the multiplication produces the value 264, which for a 64-bit unsigned integer (unsigned long long) is 0, and so the final value also becomes 0 (as anything multiplied by 0 is 0).
This could be solved by taking the modulus of the values at each step, like you suggested previously, but the value of that (prime) number should be as close as possible to 264-1, to get exact results for a larger range of values.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have tried returning the result modulo several prime numbers. The number 1000000000007 seems to be suitable, as a number bigger than it will result in fast_power(2,100) being greater than 2^100 mod (2^64-1).
fast_power(2,100) now returns 40955801449.
However, cases like fast_power(33,33) still cause overflow.
What do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about this for a while and I think what we need to do is check how many digits the result is going to take. That would be done as follows:

digits_required = floor(exponent * log10(base)) + 1

If digits_required > 19, then use modulo 1000000000007 and tell the user about this too. Otherwise, we can directly use the result.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great idea!
but I want to note that we apply modulus 1000000000007 every time we return a value in fast_power() to prevent overflow in the function.
So we may create two functions, one that uses %1000000000007 and one that doesn't. But to prevent this, I added a third parameter to the function called "mod" where we apply %1000000000007 if digits_required > 19 or else apply %10^18 which is a number that won't affect the output result.
This is how the code is right now:

if (digits_required > 19)
        {
            cout<<endl<<fast_power(base,exponent,1000000000007)<<endl;
            cout<<"*The output is modulo 10^12+7"<<endl;
        }
        else
        {
            cout<<endl<<fast_power(base,exponent,1000000000000000000)<<endl;
        }

Shall I commit with the final changes so we can close the PR and put "FastPower" behind our backs? if that's not the most convenient thing to do right now I have no problem.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, passing a third parameter is the right way instead of having 2 similar functions. But use ULLONG_MAX as the mod value in the else case instead of 1018.

Copy link
Member

@faheel faheel Jul 19, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1012+7 is not prime! So the modulo may be 0 in some cases. Use 109+7 instead.

@faheel
Copy link
Member

faheel commented Jul 19, 2017

There have been lots of suggestions recently by interested contributors to add more algorithms and data structures to the project, which I have currently put on hold because I want to close issue #74 first, as it would require a complete reorganisation of files and some refactoring of code.

I previously thought that I would work on #74 once the currently active PRs are merged, but I feel that it would take much longer that way. So, if it's not too inconvenient for you, I would like to work on issue #74 as soon as possible. The remaining work on this PR (and others) can be carried on afterwards. Once the project is refactored, all you would need to do is git fetch upstream and move the code that you wrote to the new appropriate directory.

So, what do you say?

Copy link
Member

@faheel faheel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One last change and then we're good to merge!


using namespace std;
typedef unsigned long long ull;

//Function that returns base raised to the exponent
ull fast_power (ull base,ull exponent)
ull fast_power (ull base,ull exponent,ull mod)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Give mod the default value ULLONG_MAX so that it doesn't need to be passed to the function if modulus of the result is not required.

//REQUIRE(fast_power(0,0) == undefined);
REQUIRE(fast_power(0,1,ULLONG_MAX) == 0);
REQUIRE(fast_power(1,0,ULLONG_MAX) == 1);
REQUIRE(fast_power(1,1,ULLONG_MAX) == 1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ULLONG_MAX should be removed from the tests too. You missed this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! Sorry for this.

@faheel faheel merged commit 97d92b3 into ProAlgos:master Jul 20, 2017
@faheel
Copy link
Member

faheel commented Jul 20, 2017

Congratulations on your first PR merge! 🎉 And thanks for contributing to the project 😃

@ahmad307
Copy link
Contributor Author

Thank you! 😃 Was great working with you 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants