mathmaker
0.6(alpha)
|
00001 # -*- coding: utf-8 -*- 00002 00003 # Mathmaker creates automatically maths exercises sheets 00004 # with their answers 00005 # Copyright 2006-2014 Nicolas Hainaux <nico_h@users.sourceforge.net> 00006 00007 # This file is part of Mathmaker. 00008 00009 # Mathmaker is free software; you can redistribute it and/or modify 00010 # it under the terms of the GNU General Public License as published by 00011 # the Free Software Foundation; either version 3 of the License, or 00012 # any later version. 00013 00014 # Mathmaker is distributed in the hope that it will be useful, 00015 # but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00017 # GNU General Public License for more details. 00018 00019 # You should have received a copy of the GNU General Public License 00020 # along with Mathmaker; if not, write to the Free Software 00021 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 00022 00023 import math 00024 import random 00025 import lib.maths_lib 00026 from lib import error 00027 from lib.maths_lib import * 00028 00029 # ------------------------------------------------------------------------------ 00030 # -------------------------------------------------------------------------- 00031 # ------------------------------------------------------------------------------ 00032 ## 00033 # @brief Collection of functions that return randomly results 00034 00035 00036 # -------------------------------------------------------------------------- 00037 ## 00038 # @brief Return a random integer 00039 # @param min_value The lowest possible value of the result 00040 # @param max_value The highest possible value of the result 00041 # @param **options It is possible to use a weighted table 00042 # The weighted table is a probability distribution : the sum of its values 00043 # must be 1 and the number of values must match the number of possible 00044 # results. Here's an example : [0.2, 0.4, 0.3, 0.1]. 00045 # @return An integer comprised between min_value & max_value 00046 def integer(min_value, max_value, **options): 00047 if not ('weighted_table' in options \ 00048 and len(options['weighted_table']) == max_value - min_value +1): 00049 #___ 00050 return int(math.ceil(random.random()*(max_value - min_value + 1) \ 00051 + min_value \ 00052 ) \ 00053 - 1 \ 00054 ) 00055 00056 else: 00057 # The probability scale will be calculated there from the weighted 00058 # table's date. For instance, [0.2, 0.4, 0.3, 0.1] 00059 # gives [0.2, 0.6, 0.9, 1.0] as a result 00060 proba_scale = list() 00061 current_sum = 0 00062 for i in range(len(options['weighted_table'])): 00063 current_sum += options['weighted_table'][i] 00064 proba_scale.append(current_sum) 00065 00066 random_number = random.random() 00067 last_step = 0 00068 00069 for i in range(len(proba_scale)): 00070 if random_number >= last_step and random_number < proba_scale[i]: 00071 return min_value + i 00072 else: 00073 last_step = random_number 00074 00075 return max_value 00076 00077 00078 00079 00080 00081 # -------------------------------------------------------------------------- 00082 ## 00083 # @brief Returns a '+' or a '-' 00084 # @param options plus_signs_ratio=0.3 sets the + signs ratio (default 0.5) 00085 # @return A random sign ('+' or '-') 00086 def sign(**options): 00087 plus_signs_ratio = 0.5 00088 00089 if 'plus_signs_ratio' in options \ 00090 and options['plus_signs_ratio'] >= 0 \ 00091 and options['plus_signs_ratio'] <= 1: 00092 #___ 00093 plus_signs_ratio = options['plus_signs_ratio'] 00094 00095 if (random.random() < plus_signs_ratio): 00096 return '+' 00097 else: 00098 return '-' 00099 00100 00101 00102 00103 00104 # -------------------------------------------------------------------------- 00105 ## 00106 # @brief Pops an element from the provided list 00107 # @param provided_list The list where to pop an element from 00108 # @return The randomly chosen element 00109 def pop(provided_list, **options): 00110 if not ('weighted_table' in options \ 00111 and len(options['weighted_table']) == len(provided_list)): 00112 #___ 00113 random_rank = integer(0, len(provided_list) - 1) 00114 00115 return provided_list.pop(random_rank) 00116 00117 else: 00118 i = integer(0, len(provided_list) - 1, **options) 00119 return provided_list.pop(i) 00120 00121 00122 00123 00124 00125 # -------------------------------------------------------------------------- 00126 ## 00127 # @brief Returns a randomly decimal number between 0 and 1 00128 # @return A randomly decimal number between 0 and 1 00129 def decimal_0_1(): 00130 return random.random() 00131 00132 00133 00134 00135 00136 # -------------------------------------------------------------------------- 00137 ## 00138 # @brief Returns True|False with probability distribution 0,5 - 0,5 00139 # @return True|False with probability distribution 0,5 - 0,5 00140 def heads_or_tails(): 00141 if random.random() > 0.5: 00142 return True 00143 else: 00144 return False 00145 00146 00147 00148 00149 00150 # -------------------------------------------------------------------------- 00151 ## 00152 # @brief Returns a randomly integer which is coprime to the given argument 00153 # @param n The given number 00154 # @param range As a list, numbers where to pop one what will be coprime to n. 00155 # @return A randomly integer which is coprime to the given argument 00156 def coprime_to(n, range): 00157 collected_numbers = [] 00158 00159 for number in range: 00160 if lib.maths_lib.gcd(n, number) == 1: 00161 collected_numbers.append(number) 00162 00163 if len(collected_numbers) == 0: 00164 raise error.ImpossibleAction("find a number coprime to n " \ 00165 + "in the given range.") 00166 00167 else: 00168 return pop(collected_numbers) 00169 00170 00171 00172 00173 # -------------------------------------------------------------------------- 00174 ## 00175 # @brief Returns a randomly integer which is coprime to the given argument 00176 # but possibly not to the second. 00177 # @param n The given number, what the result should be coprime to 00178 # @param p The given number, what the result should not be coprime to 00179 # @param range As a list, numbers where to look for. 00180 # @return A randomly integer which is coprime to the given argument 00181 # but possibly not to the second. 00182 def coprime_to_the_first(n, p, range): 00183 collected_numbers_coprime_to_n = [] 00184 collected_numbers_not_coprime_to_p = [] 00185 collected_numbers = [] 00186 00187 for number in range: 00188 if lib.maths_lib.gcd(n, number) == 1: 00189 collected_numbers_coprime_to_n.append(number) 00190 00191 for number in range: 00192 if lib.maths_lib.gcd(p, number) > 1: 00193 collected_numbers_not_coprime_to_p.append(number) 00194 00195 for number in collected_numbers_coprime_to_n: 00196 if number in collected_numbers_not_coprime_to_p: 00197 collected_numbers.append(number) 00198 00199 if len(collected_numbers) >= 1: 00200 return pop(collected_numbers) 00201 00202 else: 00203 if len(collected_numbers_coprime_to_n) == 0: 00204 raise error.ImpossibleAction("find a number coprime to n " \ 00205 + "in the given range.") 00206 00207 else: 00208 return pop(collected_numbers_coprime_to_n) 00209 00210 00211 00212 00213 # -------------------------------------------------------------------------- 00214 ## 00215 # @brief Returns a randomly integer which is not coprime to the given arg. 00216 # @warning If the given argument is a prime number, it'll be difficult 00217 # to find an integer which is not coprime to it, especillay if 00218 # the range is low. 00219 # @param n The given number, what the result must be coprime to 00220 # @param range As a list, numbers where to look for. 00221 # @return A randomly integer which is not coprime to the given argument. 00222 def not_coprime_to(n, range, **options): 00223 collected_numbers = [] 00224 check = False 00225 avoid = 0 00226 00227 if 'excepted' in options: 00228 check = True 00229 avoid = options['excepted'] 00230 00231 for number in range: 00232 if lib.maths_lib.gcd(n, number) != 1: 00233 if not (check and number == avoid): 00234 collected_numbers.append(number) 00235 00236 if len(collected_numbers) == 0: 00237 raise error.ImpossibleAction("find a number not coprime to n " \ 00238 + "in the given range.") 00239 00240 else: 00241 return pop(collected_numbers) 00242 00243 00244 00245 00246 # -------------------------------------------------------------------------- 00247 ## 00248 # @brief When given a list of objects, returns a randomly mixed list of the 00249 # @brief same objects. Must not return in the same order than given one. 00250 # @param objects_list 00251 # @return a list 00252 def mix(objects_list): 00253 result = [] 00254 00255 order_changed = False 00256 00257 for i in range(len(objects_list) - 2): 00258 j = integer(0, len(objects_list) - 1) 00259 00260 if i != j and order_changed == False: 00261 order_changed = True 00262 00263 next_to_add = objects_list[j].clone() 00264 trash = objects_list.pop(j) 00265 00266 result.append(next_to_add) 00267 00268 if order_changed: 00269 next_to_add = pop(objects_list).clone() 00270 result.append(next_to_add) 00271 next_to_add = pop(objects_list).clone() 00272 result.append(next_to_add) 00273 00274 else: 00275 next_to_add = objects_list[1].clone() 00276 result.append(next_to_add) 00277 next_to_add = objects_list[0].clone() 00278 result.append(next_to_add) 00279 00280 00281 return result 00282 00283 00284 00285 00286