Skip to content

Add a factorial function #46391

Closed
Closed

Description

@dtorp
mannequin
BPO 2138
Nosy @birkenfeld, @rhettinger, @terryjreedy, @mdickinson, @devdanzin

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields:

assignee = 'https://github.com/rhettinger'
closed_at = <Date 2008-06-09.06:55:31.132>
created_at = <Date 2008-02-18.10:09:27.896>
labels = ['type-feature', 'library']
title = 'Add a factorial function'
updated_at = <Date 2010-05-12.19:57:34.307>
user = 'https://bugs.python.org/dtorp'

bugs.python.org fields:

activity = <Date 2010-05-12.19:57:34.307>
actor = 'mark.dickinson'
assignee = 'rhettinger'
closed = True
closed_date = <Date 2008-06-09.06:55:31.132>
closer = 'rhettinger'
components = ['Library (Lib)']
creation = <Date 2008-02-18.10:09:27.896>
creator = 'dtorp'
dependencies = []
files = []
hgrepos = []
issue_num = 2138
keywords = []
message_count = 35.0
messages = ['62520', '62534', '62535', '62536', '62539', '62541', '62549', '62551', '62552', '62553', '62555', '62557', '62674', '62691', '62707', '62729', '62761', '63805', '63816', '63818', '63822', '63823', '63828', '63961', '63969', '64913', '67571', '67572', '67583', '67588', '67859', '68355', '69831', '73278', '105605']
nosy_count = 13.0
nosy_names = ['georg.brandl', 'rhettinger', 'terry.reedy', 'jafo', 'phr', 'mark.dickinson', 'dtorp', 'alanmcintyre', 'ajaksu2', 'avalind', 'ilan', 'uzi', 'maix']
pr_nums = []
priority = 'normal'
resolution = 'accepted'
stage = None
status = 'closed'
superseder = None
type = 'enhancement'
url = 'https://bugs.python.org/issue2138'
versions = ['Python 2.6']

Activity

  1. dtorp commented on Feb 18, 2008

    dtorpmannequin
    MannequinAuthor

    Add a factorial method. Everybody understands what it means before
    they are out of high school and it comes up all the time in statistics
    and combinatorics. Ruby has a factorial method and heck even basic
    calculators have a factorial key.

    print n.factorial()

    Maybe raise ValueError if n is negative.

  2. added
    interpreter-core(Objects, Python, Grammar, and Parser dirs)
    type-featureA feature request or enhancement
    on Feb 18, 2008
  3. alanmcintyre commented on Feb 18, 2008

    alanmcintyremannequin
    Mannequin

    It seems like most of the methods on integers are for two-argument
    operations (add, mul, div, etc.), while a lot of single-argument
    operations are in the math module. If this gets added would it fit
    better as a function in the math module?

    I have to say a factorial function is something I've found myself
    writing several times, so I certainly wouldn't mind having one included.
    Yeah, it's a one-liner, but so is hypot, and that's already in the
    stdlib. And most calculators don't have a hypot button. ;)

  4. mdickinson commented on Feb 18, 2008

    @mdickinson
    Member

    I don't think it would be appropriate to add this as a method of int;
    it seems like unnecessary clutter on a core type.

    Perhaps a math.factorial function could be considered? Historically,
    the math module has just been a way to wrap the (mostly floating-point)
    libm functions, but I think that may be changing: there's at least some
    interest in putting gcd into the math module, for example.

    David, any chance you could come up with a patch implementing
    math.factorial?

  5. mdickinson commented on Feb 18, 2008

    @mdickinson
    Member

    Yeah, it's a one-liner, but so is hypot

    Except that hypot is not a one-liner, if you want to get edge cases
    right. (Consider hypot(1e200, 1e200), or hypot(1e-200, 1e-200).)

  6. alanmcintyre commented on Feb 18, 2008

    alanmcintyremannequin
    Mannequin

    Except that hypot is not a one-liner, if you want to get edge cases
    right.

    Ah, true; thanks for pointing that out.

    Should there be some upper limit on the argument math.factorial would
    take? At the moment I can't think of any reason for picking a given
    limit, except perhaps execution time. (10**4)! can be done in about 1
    second on my old & slow laptop; are there realistic use cases for
    numbers much bigger than that?

  7. mdickinson commented on Feb 18, 2008

    @mdickinson
    Member

    Should there be some upper limit on the argument math.factorial would
    take?

    I'd say not. Any such limit would be artificial, and an arbitrary
    choice. Just let the natural time and space requirements be the
    limiting factor.

  8. rhettinger commented on Feb 18, 2008

    @rhettinger
    Contributor

    Since the domain and range are ints/longs, this makes more sense as a
    method on <type 'int'> than as a math module function. The other math
    module functions mostly have real valued inputs and mostly have
    counterparts in cmath.

    IIRC, Tim wanted the math module to maintain that theme and not become
    a home to every function that seems a bit "mathematical". I share the
    sentiment -- it would be nice for the math module to retain its unified
    theme during its growth spurt.

  9. mdickinson commented on Feb 18, 2008

    @mdickinson
    Member

    Since the domain and range are ints/longs, this makes more sense as a
    method on <type 'int'> than as a math module function.

    Fair enough. Raymond, do you have any thoughts on where a gcd
    implementation might most usefully live? Should it stay in
    fractions.py, or is there a case for moving it somewhere more central?

  10. rhettinger commented on Feb 18, 2008

    @rhettinger
    Contributor

    gcd() is probably fine where it is. People learn about GCD and LCM
    where they first learn fractions. So, there is a strong association.

  11. mdickinson commented on Feb 18, 2008

    @mdickinson
    Member

    The other issue here is that I see factorial as being the thin end of
    the
    wedge. If factorial were implemented, it would probably only be on the
    order of minutes before people wanted to know where the binomial()
    function was.

    So it seems to me that either there should be a good single place for
    all
    integer-related math, (gcd, xgcd, binomial, factorial, ...) or we should
    leave this for third-party modules for the moment.

  12. rhettinger commented on Feb 19, 2008

    @rhettinger
    Contributor

    I wouldn't worry about that so much -- our job is to provide good
    primitives.

  13. dtorp commented on Feb 19, 2008

    dtorpmannequin
    MannequinAuthor

    Mr. Dickinson thank you for doing this. I do not know how to help with
    a patch. If it helps, here is the code I use in python:

    def factorial(n, _known=[1]):
        assert isinstance(n, int), "Need an integer. This isn't a gamma"
        assert n >= 0, "Sorry, can't factorilize a negative"
        assert n < 1000, "No way! That's too large"
        try:
            return _known[n]
        except IndexError:
            pass
        for i in range(len(_known), n+1):
            _known.append(_known[-1] * i)
        return _known[n]

    When the assertions are turned-off, this runs pretty fast.

  14. phr commented on Feb 22, 2008

    phrmannequin
    Mannequin

    I like the idea of having some integer math functions like this in the
    stdlib; whether in the math module or elsewhere doesn't matter much. In
    particular I remember having to implement xgcd on several separate
    occasions for unrelated applications, each time requiring about the same
    head scratching as before, and wishing it was in the stdlib so that
    could be avoided. This type of function is complicated enough to not
    rattle immediately off the fingertips, but not complicated enough to be
    worth looking up in a reference book.

    http://bugs.python.org/issue457066 has some discussion of xgcd and
    modular inverses.

  15. 17 remaining items

  16. terryjreedy commented on Apr 4, 2008

    @terryjreedy
    Member

    The fact that other languages have factorial does not in itself impress
    me. What is the actual use case other than illustrating computational
    induction (whether by iteration or recursion) and for calculating
    binomial coefficients?

    I don't really like the idea of making factorial a method for the same
    reason that exp, sin, ....... are not methods of float: there is no end.
    People should be able to learn basic classes without specialized
    functions attached.

  17. uzi commented on May 31, 2008

    uzimannequin
    Mannequin

    It's a simplified version, but why not something like this:

    import operator
    def factorial(num):
        return reduce(operator.mul, range(1, num+1))

    A product() function could also be done similarly.

  18. uzi commented on May 31, 2008

    uzimannequin
    Mannequin

    Or slightly better:

    from operator import mul
    def factorial(num):
        return reduce(mul, range(2, num+1), 1)
  19. mdickinson commented on May 31, 2008

    @mdickinson
    Member

    Contrary to what I said above, I'm not going to find time for this
    before the beta. Anyone else want to have a go at producing a patch?

    A simple implementation would be fine---the algorithm could be tweaked
    for speed later.

  20. self-assigned this
    on May 31, 2008
  21. rhettinger commented on May 31, 2008

    @rhettinger
    Contributor

    I've got in from here.

  22. rhettinger commented on Jun 9, 2008

    @rhettinger
    Contributor

    Added math.factorial() in r64050.

  23. mdickinson commented on Jun 18, 2008

    @mdickinson
    Member

    It looks like there's a refcounting bug in the code: if the call to
    PyNumber_Multiply fails then iobj gets DECREF'd twice. This means that a
    keyboard interrupt of factorial() can exit the interpreter:

    Python 2.6a3+ (trunk:64341M, Jun 17 2008, 13:19:01) 
    [GCC 4.0.1 (Apple Inc. build 5465)] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    >>> from math import factorial
    [36374 refs]
    >>> factorial(10**9)
    ^CFatal Python error: 
    /Users/dickinsm/python_source/trunk/Modules/mathmodule.c:562 object at 
    0x81f63c has negative ref count -1
    Abort trap
  24. birkenfeld commented on Jul 16, 2008

    @birkenfeld
    Member

    That was fixed by Raymond in 64365.

  25. maix commented on Sep 15, 2008

    maixmannequin
    Mannequin

    I think the unit test is wrong, or at least not as is intended:

    You put some numbers in values and shuffle them. But you don't use them
    but just range(10) for testing.

  26. mdickinson commented on May 12, 2010

    @mdickinson
    Member

    maix: good point. Fixed in revisions r81126 through r81129.

  27. transferred this issue fromon Apr 10, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

stdlibStandard Library Python modules in the Lib/ directorytype-featureA feature request or enhancement

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions