mathmaker
0.4(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 from lib import * 00024 from Q_Structure import Q_Structure 00025 from core.base_calculus import * 00026 from core.calculus import * 00027 00028 AVAILABLE_Q_KIND_VALUES = \ 00029 {'fraction_simplification' : ['default'], 00030 'fractions_sum' : ['default'], 00031 'fractions_product' : ['default'], 00032 'fractions_quotient' : ['default'] 00033 00034 } 00035 00036 FRACTION_PRODUCT_AND_QUOTIENT_TABLE = [0.07, #2 00037 0.08, #3 00038 0.08, #4 00039 0.08, #5 00040 0.08, #6 00041 0.07, #7 00042 0.08, #8 00043 0.08, #9 00044 0.09, #10 00045 0.02, #11 00046 0.05, #12 00047 0.02, #13 00048 0.05, #14 00049 0.03, #15 00050 0.04, #16 00051 0.01, #17 00052 0.03, #18 00053 0.01, #19 00054 0.03 #20 00055 ] 00056 00057 FRACTIONS_SUMS_TABLE = [([1, 2], 15), 00058 ([1, 3], 15), 00059 ([1, 4], 15), 00060 ([1, 5], 15), 00061 ([1, 6], 15), 00062 ([1, 7], 15), 00063 ([2, 3], 17), 00064 ([2, 5], 10), 00065 ([2, 7], 7), 00066 ([3, 4], 9), 00067 ([3, 5], 7), 00068 ([3, 7], 5), 00069 ([4, 5], 5), 00070 ([4, 7], 3), 00071 ([5, 6], 4), 00072 ([5, 7], 4), 00073 ([6, 7], 3)] 00074 00075 FRACTIONS_SUMS_SCALE_TABLE = [0.02, #(1, 2) 00076 0.02, 00077 0.02, 00078 0.01, 00079 0.005, 00080 0.005, #(1, 7) 00081 0.21, #(2, 3) 00082 0.14, #(2, 5) 00083 0.02, #(2, 7) 00084 0.21, #(3, 4) 00085 0.14, #(3, 5) 00086 0.01, #(3, 7) 00087 0.16, #(4, 5) 00088 0.01, #(4, 7) 00089 0.01, #(5, 6) 00090 0.005, #(5, 7) 00091 0.005] #(6, 7) 00092 00093 # ------------------------------------------------------------------------------ 00094 # -------------------------------------------------------------------------- 00095 # ------------------------------------------------------------------------------ 00096 ## 00097 # @class Q_Calculation 00098 # @brief All kinds of numeric calculation questions (Fractions Sums, 00099 # Products, numeric Sums & Products with priorities etc.) 00100 class Q_Calculation(Q_Structure): 00101 00102 00103 00104 00105 00106 # -------------------------------------------------------------------------- 00107 ## 00108 # @brief Constructor. 00109 # @param embedded_machine The machine to be used 00110 # @param **options Any options 00111 # @return One instance of question.Q_Calculation 00112 def __init__(self, embedded_machine, q_kind='default_nothing', **options): 00113 self.derived = True 00114 00115 # The call to the mother class __init__() method will set the 00116 # fields matching optional arguments which are so far : 00117 # self.q_kind, self.q_subkind 00118 # plus self.machine, self.options (modified) 00119 Q_Structure.__init__(self, embedded_machine, 00120 q_kind, AVAILABLE_Q_KIND_VALUES, 00121 **options) 00122 # The purpose of this next line is to get the possibly modified 00123 # value of **options 00124 options = self.options 00125 00126 # That's the number of the question, not of the expressions it might 00127 # contain ! 00128 self.number = "" 00129 if 'number_of_questions' in options: 00130 self.number = options['number_of_questions'] 00131 00132 self.objct = None 00133 00134 # 1st OPTION 00135 if q_kind == 'fraction_simplification': 00136 root = randomly.integer(2, 00137 19, 00138 weighted_table=[0.225, 0.225, 0, 0.2, 00139 0, 0.2, 0, 0, 0, 0.07, 00140 0, 0.0375, 0, 0, 0, 00141 0.0375, 0, 0.005 00142 ]) 00143 00144 factors_list = [j+1 for j in xrange(10)] 00145 00146 ten_power_factor1 = 1 00147 ten_power_factor2 = 1 00148 00149 if 'with_ten_powers' in options \ 00150 and is_.a_number(options['with_ten_powers']) \ 00151 and options['with_ten_powers'] <= 1 \ 00152 and options['with_ten_powers'] >= 0: 00153 #___ 00154 if randomly.decimal_0_1() < options['with_ten_powers']: 00155 ten_powers_list = [10, 10, 100, 100] 00156 ten_power_factor1 = randomly.pop(ten_powers_list) 00157 ten_power_factor2 = randomly.pop(ten_powers_list) 00158 00159 00160 00161 self.objct = Fraction(('+', 00162 root*randomly.pop(factors_list) \ 00163 * ten_power_factor1, 00164 root*randomly.pop(factors_list) \ 00165 * ten_power_factor2 00166 )) 00167 00168 # 2d & 3d OPTIONS 00169 # Fractions Products | Quotients 00170 elif q_kind == 'fractions_product' \ 00171 or q_kind == 'fractions_quotient': 00172 #___ 00173 # In some cases, the fractions will be generated 00174 # totally randomly 00175 if randomly.decimal_0_1() < 0: 00176 lil_box = [n+2 for n in xrange(18)] 00177 a = randomly.pop(lil_box, 00178 weighted_table= \ 00179 FRACTION_PRODUCT_AND_QUOTIENT_TABLE) 00180 b = randomly.pop(lil_box, 00181 weighted_table= \ 00182 FRACTION_PRODUCT_AND_QUOTIENT_TABLE) 00183 00184 lil_box = [n+2 for n in xrange(18)] 00185 c = randomly.pop(lil_box, 00186 weighted_table= \ 00187 FRACTION_PRODUCT_AND_QUOTIENT_TABLE) 00188 d = randomly.pop(lil_box, 00189 weighted_table= \ 00190 FRACTION_PRODUCT_AND_QUOTIENT_TABLE) 00191 00192 f1 = Fraction((randomly.sign(plus_signs_ratio=0.75), 00193 Item((randomly.sign(plus_signs_ratio=0.80), 00194 a)), 00195 Item((randomly.sign(plus_signs_ratio=0.80), 00196 b)) 00197 )) 00198 00199 f2 = Fraction((randomly.sign(plus_signs_ratio=0.75), 00200 Item((randomly.sign(plus_signs_ratio=0.80), 00201 c)), 00202 Item((randomly.sign(plus_signs_ratio=0.80), 00203 d)) 00204 )) 00205 00206 #f1 = f1.simplified() 00207 #f2 = f2.simplified() 00208 00209 # In all other cases (80%), we'll define a "seed" a plus two 00210 # randomly numbers i and j to form the Product | Quotient : 00211 # a×i / b × c / a × j 00212 # Where b is a randomly number coprime to a×i 00213 # and c is a randomly number coprime to a×j 00214 else: 00215 a = randomly.integer(2, 8) 00216 lil_box = [i+2 for i in xrange(7)] 00217 i = randomly.pop(lil_box) 00218 j = randomly.pop(lil_box) 00219 00220 b = randomly.coprime_to(a*i, [n+2 for n in xrange(15)]) 00221 c = randomly.not_coprime_to(b, 00222 [n+2 for n in xrange(30)], 00223 excepted=a*j) 00224 00225 f1 = Fraction((randomly.sign(plus_signs_ratio=0.75), 00226 Item((randomly.sign(plus_signs_ratio=0.80), 00227 a*i)), 00228 Item((randomly.sign(plus_signs_ratio=0.80), 00229 b)) 00230 )) 00231 00232 f2 = Fraction((randomly.sign(plus_signs_ratio=0.75), 00233 Item((randomly.sign(plus_signs_ratio=0.80), 00234 c)), 00235 Item((randomly.sign(plus_signs_ratio=0.80), 00236 a*j)) 00237 )) 00238 00239 if randomly.heads_or_tails(): 00240 f3 = f1.clone() 00241 f1 = f2.clone() 00242 f2 = f3.clone() 00243 00244 if q_kind == 'fractions_quotient': 00245 f2 = f2.invert() 00246 00247 if q_kind == 'fractions_product': 00248 self.objct = Product([f1, f2]) 00249 00250 elif q_kind == 'fractions_quotient': 00251 self.objct = Quotient(('+', f1, f2, 1, 00252 'use_divide_symbol' 00253 )) 00254 00255 00256 # 4th OPTION 00257 # Fractions Sums 00258 elif q_kind == 'fractions_sum': 00259 randomly_position = randomly.integer(0, 00260 16, 00261 weighted_table=\ 00262 FRACTIONS_SUMS_SCALE_TABLE) 00263 00264 chosen_seed_and_generator = FRACTIONS_SUMS_TABLE[randomly_position] 00265 00266 00267 seed = randomly.integer(2, chosen_seed_and_generator[1]) 00268 00269 # The following test is only intended to avoid having "high" 00270 # results too often. We just check if the common denominator 00271 # will be higher than 75 (arbitrary) and if yes, we redetermine 00272 # it once. We don't do it twice since we don't want to totally 00273 # forbid high denominators. 00274 if seed * chosen_seed_and_generator[0][0] \ 00275 * chosen_seed_and_generator[0][1] >= 75: 00276 #___ 00277 seed = randomly.integer(2, chosen_seed_and_generator[1]) 00278 00279 lil_box = [0, 1] 00280 gen1 = chosen_seed_and_generator[0][lil_box.pop()] 00281 gen2 = chosen_seed_and_generator[0][lil_box.pop()] 00282 00283 den1 = Item(gen1*seed) 00284 den2 = Item(gen2*seed) 00285 00286 temp1 = randomly.integer(1, 20) 00287 temp2 = randomly.integer(1, 20) 00288 00289 num1 = Item(temp1 / gcd(temp1, gen1*seed)) 00290 num2 = Item(temp2 / gcd(temp2, gen2*seed)) 00291 00292 f1 = Fraction((randomly.sign(plus_signs_ratio=0.7), 00293 num1, 00294 den1)) 00295 f2 = Fraction((randomly.sign(plus_signs_ratio=0.7), 00296 num2, 00297 den2)) 00298 00299 self.objct = Sum([f1.simplified(), 00300 f2.simplified()]) 00301 00302 # 5th 00303 # still to imagine :o) 00304 00305 # Creation of the expression : 00306 number = 0 00307 if 'expression_number' in options \ 00308 and is_.a_natural_int(options['expression_number']): 00309 #___ 00310 number = options['expression_number'] 00311 self.expression = Expression(number, self.objct) 00312 00313 00314 00315 # -------------------------------------------------------------------------- 00316 ## 00317 # @brief Returns the text of the question as a str 00318 def text_to_str(self): 00319 M = self.machine 00320 00321 return M.write_math_style1(M.type_string(self.expression)) 00322 00323 00324 00325 00326 00327 00328 00329 # -------------------------------------------------------------------------- 00330 ## 00331 # @brief Returns the answer of the question as a str 00332 def answer_to_str(self): 00333 M = self.machine 00334 00335 result = "" 00336 00337 while self.objct != None: 00338 result += M.write_math_style1(M.type_string(self.expression)) 00339 00340 self.objct = self.objct.calculate_next_step() 00341 if self.objct != None: 00342 self.expression = Expression(self.expression.name, 00343 self.objct) 00344 00345 00346 return result 00347 00348 00349 00350 00351 00352 00353