> To try to find factors of z, using w.
>
>>>>
>
> * What are the inputs? What is 'z', and what is 'w'?
>
> ******
>
> z is the number to be factores.
>
> w is a "witness", a number that may help find the factors of z.

[My apologies in advance; this message is a bit long.]



Hi Kermit,

Ok, good.  You should add this as a comment to the function's header, so 
that other people can see this.  Here is an example:

def strongfac(z, w):
     """z is the number to be factored.
        w is a witness that may help find the factors of z.
     """
     ## rest of function body.

At the very beginning of the function body, we're allowed to make a string 
that acts as function documentation.  Python programmers expect this kind 
of "documentation string" at the head of a function, so that they're 
prepared for what comes next.



> * What are the outputs? What is the return value of strongfac?
>
> The output is the return value of strongfac.
> fac = [ x, y, difficulty of factoring z using w]
> or
> fac = [0,0,0] if strongfac could not factor z using w.

Great!  Ok, I'm assuming that 'x' and 'y' are two arbitrary factors of 
'z'.  Are there other properties about x and y that you want to state, 
such as: "x is always prime", or do we make no guarantees about this?



At this point, I'm distilling what you've said into a documentation 
string:

def strongfac(z, w):
     """strongfac: number number -> [x, y, difficulty]

     Factors z into two pieces, returning those two pieces as well as the
     difficulty in factoring z.  w is a witness that may help find the
     factors of z.

     If no factors can be found, returns [0, 0, 0].
     """
     ## rest of function body.


Does this make sense so far?  The point is to make it easy for humans to 
understand your program.




>> # face = [x,y,0]
> [some code cut]
>> # face[0] = x
>> # face[1] = y
>> # face[2] = 0
>
>> The three assignments to face[0] through face[2] don't do anything 
>> effective and clutter the code.
>
> In fact, I knew that.  I inserted it in an attempt to overide the 
> apparent bug that I saw.
>
> Sometimes it worked.

This is a bad sign.  It should not have worked.  *grin*

Next time you hit something mysterious like this, mention it to the list. 
It'll be a good puzzle for the others here to take a look at.  Your code 
should be as rational as possible.  Don't just add code in hoping that 
it'll fix some problem: try to understand why it's not working first. 
Look for root causes.




>> In fermat(), there are magic numbers:
> [cut]
>> These seem pretty arbitrary. Can you explain them?

> Yes.  These are limits of ranges.  I had not yet evolved the module to 
> replace the range limits with parameters.



About strongfac():

> Have you considered breaking those out as independent helper functions?
>
> I have considered it.

Consider it more strongly.  *grin*



Here, I'll help you get started.  strongfac() starts off looking something 
like this:

#################################################################
def strongfac(z,w):
     x = gcd(z,w-1)
     if x > 1:
         if x < z:
             y = z/x
             return [x,y,0]
     w2 = (w * w)%z
     s = ksqrt(w2)
     if w2 == s * s:
         if s != w:
             x = gcd(z,kabs(w2 - s))
             if x > 1:
                 if x < z:
                     return [x,y,0]
     ## other tests
################################################################

(I'm trimming out the print statements just to keep this small.)


Those two tests, the P-1 and square=square tests, can be broken out into 
two separate functions.  Let's see what this might look like:

############################################################
def check_p_minus_1(z, w):
     """Checks to see if z can be factored by the P-1 Method.
     If so, returns [x, y, 0]; otherwise, returns [0, 0, 0].
     """
     x = gcd(z,w-1)
     if x > 1:
         if x < z:
             y = z/x
             return [x,y,0]
     return [0, 0, 0]
############################################################

This "refactoring" allows us to look at check_p_minus_1's action in 
isolation, and also makes it easier to ask questions like: what happens if 
x > z?  Is it really possible that the return value from gcd() is bigger 
than the initial inputs to gcd()?  (As far as I understand gcd(), NO!)

So this refactoring is useful just as a matter of letting someone just 
look at a small bit of code and understand it completely.  As far as I can 
understand, check_p_minus_1() should work even if it's simplified to:

##########################
def check_p_minus_1(z, w):
     x = gcd(z, w-1)
     if x != 1:
         return [x, z/x, 0]
     return [0, 0, 0]
##########################




Anyway, once we break out the P-1 check out into a separate function, we 
can rewrite strongfac() to use check_p_minus_1():

################################
def strongfac(z, w):
     face = check_p_minus_1(z, w)
     if face != [0, 0, 0]:
         return face
     ## ... other tests here
################################


Can you try this?  Can you try breaking out the square=square test in a 
similar way to how I treated the check_p_minus_1() code?




The high level goal here is to turn strongfac() into a very simple looking 
function.  In pseudocode, it'll look something like:

############################
def strongfac(z, w):
     use check_p_minus_1 test and return if it succeeds
     use square=square test and return if it succeeds
     ...
############################

This will make strongfac very regular to read.  It'll also make it much 
easier to trace why one of your return values isn't returning what you 
want.
_______________________________________________
Tutor maillist  -  Tutor@python.org
http://mail.python.org/mailman/listinfo/tutor

Reply via email to