Feat(Core/API): Random generation methods for numbers

This commit is contained in:
Bertrand Benjamin 2018-12-22 08:42:31 +01:00
parent fd49a6c987
commit f04e221f70
2 changed files with 141 additions and 26 deletions

View File

@ -14,6 +14,7 @@ from decimal import Decimal as _Decimal
from random import random, randint from random import random, randint
from .token import Token from .token import Token
from ...core.arithmetic import gcd from ...core.arithmetic import gcd
from ...core.random import filter_random
from ...core.MO import MO, MOnumber from ...core.MO import MO, MOnumber
from ...core.MO.fraction import MOFraction from ...core.MO.fraction import MOFraction
@ -58,7 +59,7 @@ class Integer(Token):
min_value = -10, min_value = -10,
max_value = 10, max_value = 10,
rejected = [0, 1], rejected = [0, 1],
reject_callbacks=[], accept_callbacks=[],
): ):
""" Generate a random Integer """ Generate a random Integer
@ -66,14 +67,11 @@ class Integer(Token):
:param min_value: minimum value :param min_value: minimum value
:param max_value: maximum value :param max_value: maximum value
:param rejected: rejected values :param rejected: rejected values
:param reject_callbacks: list of function for value rejection :param accept_callbacks: list of function for value acceptation
""" """
conditions = [lambda x: x in rejected] + reject_callbacks candidate = filter_random(min_value, max_value,
rejected, accept_callbacks)
candidate = randint(min_value, max_value)
while any(c(candidate) for c in conditions):
candidate = randint(min_value, max_value)
return Integer(candidate, name) return Integer(candidate, name)
@ -181,10 +179,10 @@ class Fraction(Token):
name="", name="",
fix_num="", fix_num="",
min_num=-10, max_num=10, rejected_num=[0], min_num=-10, max_num=10, rejected_num=[0],
reject_num_callbacks=[], accept_num_callbacks=[],
fix_denom="", fix_denom="",
min_denom=-10, max_denom=10, rejected_denom=[0, 1, -1], min_denom=-10, max_denom=10, rejected_denom=[0, 1, -1],
reject_denom_callbacks=[], accept_denom_callbacks=[],
irreductible=False, irreductible=False,
not_integer=True not_integer=True
): ):
@ -195,39 +193,37 @@ class Fraction(Token):
:param min_num: minimum value for the numerator :param min_num: minimum value for the numerator
:param max_num: maximum value for the numerator :param max_num: maximum value for the numerator
:param rejected_num: rejected values for the numerator :param rejected_num: rejected values for the numerator
:param reject_num_callbacks: list of function for numerator rejection :param accept_num_callbacks: list of function for numerator rejection
:param fix_denom: if set, the denomerator will get this value :param fix_denom: if set, the denomerator will get this value
:param min_denom: minimum value for the denominator :param min_denom: minimum value for the denominator
:param max_denom: maximum value for the denominator :param max_denom: maximum value for the denominator
:param rejected_denom: rejected values for the denominator :param rejected_denom: rejected values for the denominator
:param reject_denom_callbacks: list of function for denomerator rejection :param accept_denom_callbacks: list of function for denomerator rejection
:param irreductible: is the generated fraction necessary irreductible :param irreductible: is the generated fraction necessary irreductible
:param not_integer: can the generated fraction be egal to an interger :param not_integer: can the generated fraction be egal to an interger
""" """
if fix_num == "": if fix_num == "":
conditions = [lambda x: x in rejected_denom] + reject_num_callbacks num = filter_random(min_num, max_num,
rejected_num,
num = randint(min_num, max_num) accept_num_callbacks)
while any(c(num) for c in conditions):
num = randint(min_num, max_num)
else: else:
num = fix_num num = fix_num
if fix_denom == "": if fix_denom == "":
conditions = [lambda x: x in rejected_denom] + reject_denom_callbacks accept_callbacks = accept_denom_callbacks
if irreductible: if irreductible:
def not_prime_with_num(denom): def prime_with_num(denom):
return gcd(num, denom) != 1 return gcd(num, denom) == 1
conditions.append(not_prime_with_num) accept_callbacks.append(prime_with_num)
if not_integer: if not_integer:
def divise_num(denom): def not_divise_num(denom):
return num % denom == 0 return num % denom != 0
conditions.append(divise_num) accept_callbacks.append(not_divise_num)
denom = randint(min_denom, max_denom) denom = filter_random(min_denom, max_denom,
while any(c(denom) for c in conditions) : rejected_denom,
denom = randint(min_denom, max_denom) accept_callbacks)
else: else:
denom = fix_denom denom = fix_denom

View File

@ -0,0 +1,119 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vim:fenc=utf-8
#
# Copyright © 2017 lafrite <lafrite@Poivre>
#
# Distributed under terms of the MIT license.
"""
Function to create random things
"""
from random import randint, choice
__all__ = ["reject_random", "filter_random", "FilterRandom"]
def reject_random(min_value = -10,
max_value = 10,
rejected = [0, 1],
accept_callbacks=[],
):
""" Generate a random integer with the rejection method
:param name: name of the Integer
:param min_value: minimum value
:param max_value: maximum value
:param rejected: rejected values
:param accept_callbacks: list of function for value rejection
"""
conditions = [lambda x: x not in rejected] + accept_callbacks
candidate = randint(min_value, max_value)
while not all(c(candidate) for c in conditions):
candidate = randint(min_value, max_value)
return candidate
def filter_random(min_value = -10,
max_value = 10,
rejected = [0, 1],
accept_callbacks=[],
):
""" Generate a random integer by filtering then choosing a candidate
:param name: name of the Integer
:param min_value: minimum value
:param max_value: maximum value
:param rejected: rejected values
:param accept_callbacks: list of function for value rejection
"""
candidates = set(range(min_value, max_value+1))
candidates = {c for c in candidates if c not in rejected}
candidates = [candidate for candidate in candidates \
if all(c(candidate) for c in accept_callbacks)]
if len(candidates) == 0:
raise OverflowError("There is no candidates for this range and those conditions")
return choice(candidates)
class FilterRandom(object):
""" Integer random generator which filter then choose candidate
"""
# TODO: Faire un cache pour éviter de reconstruire les listes à chaque fois |ven. déc. 21 19:07:42 CET 2018
def __init__(self,
rejected = [0, 1],
accept_callbacks=[],
min_value = -10,
max_value = 10,
):
self.conditions = (lambda x: x not in rejected,) + tuple(accept_callbacks)
self._min = min_value
self._max = max_value
candidates = set(range(self._min, self._max+1))
self._candidates = { candidate for candidate in candidates \
if all(c(candidate) for c in self.conditions) }
def add_candidates(self, low, high):
""" Add candidates between low and high to _candidates """
if low < self._min:
self._min = low
useless_low = False
else:
useless_low = True
if high > self._max:
self._max = high
useless_high = False
else:
useless_high = True
if not(useless_low and useless_high):
candidates = set(range(low, high+1))
self._candidates = self._candidates.union({
candidate for candidate in candidates \
if all(c(candidate) for c in self.conditions) \
})
def candidates(self, min_value=-10, max_value=10):
""" Return candidates between min_value and max_value """
return [c for c in self._candidates if (c > min_value and c < max_value)]
def __call__(self, min_value=-10, max_value=10):
""" Randomly choose on candidate """
self.add_candidates(min_value, max_value)
return choice(self.candidates(min_value, max_value))
# -----------------------------
# Reglages pour 'vim'
# vim:set autoindent expandtab tabstop=4 shiftwidth=4:
# cursor: 16 del