google / mathsteps

Step by step math solutions for everyone
https://socratic.org
Apache License 2.0
2.12k stars 278 forks source link

Better break-up fraction #76

Open hmaurer opened 7 years ago

hmaurer commented 7 years ago

I have no idea how to go about this in a general way, but currently break-up fraction isn't very smart.

For example, (2x+3)/(2x+2) simplifies to 2 x / (2 x + 2) + 3 / (2 x + 2), which is arguable more complex. Instead, it might be better if it did: (2x+3)/(2x+2) -> (2x+2 + 1)/(2x+2) -> (2x+2)/(2x+2) + 1/(2x+2) -> 1 + 1/(2x+2).

Thoughts?

evykassirer commented 7 years ago

agreed! that'd be sweet.

we could start with a rule that handles only this case, where the denominator can be found in the numerator

so here you'd check if an x term is in the numerator and if a constant term is in the numerator, and then split it up into 2x + 2 and the rest of the numerator.

I think we should keep the current break-up fraction functionality, but add this as a more specific check before we do the general one, so we do the better one instead if it's possible.

How's that sound?

eltonlaw commented 7 years ago

Hi, if there's nobody working on this currently, could I take it? I'll start with implementing the case mentioned above.

evykassirer commented 7 years ago

Yes! I'd love to get this stuff into mathsteps.

It's definitely a more open ended problem, so starting with what I described sounds good, and I can take a look and then we can discuss from there? Happy to help if you have any questions about the code or how the new steps will work

And I'm definitely down to brainstorm further things than the case above if you want someone to bounce ideas around with

PS I noticed you're a UW student (heh) and if you're on campus and want a mathsteps sticker for getting involved, lemme know - I'm around until the 24th ^^

eltonlaw commented 7 years ago

Hi, awesome, yes please can we brainstorm a bit?

I'm a bit stuck on the rule formulation though, how general would you like this rule to be? Like the main idea is by adding a new term we should be able to partially factor the equation into an integer right? It makes sense for linear variables, but would we want to extend that to n >=2 ? Like would this be the proper course of action: (2x^3+ 2)/(x^3+4x^2+2x+5) => 2 - (8x^2+4x+8)/(x^3+4x^2+2x+5). Would this be overcomplicating things?

Either way, I think I would go about first checking if the highest power variable in the numerator and denominator were == 1 (or n), then doing numerator - denominator to find out how many copies of the denominator can fit into the numerator. From there, we would get the integers(# of full fits) and then whatever remainder we had earlier divided by the denominator.

Anyways, what do you think? How much should this rule cover - just x^1 or for all x^n? Am I thinking about the implementation for this correctly?

Oh, yeah I'm not on campus often because I'm closer to the Laurier side (I'm a laurier student), but thanks! I'll let you know haha!

aliang8 commented 7 years ago

Hi! I think that "breaking-up fraction" is not necessarily the feature that we want in mathsteps. It really doesn't serve much purpose. Instead, I agreed that we should implement the suggested change. This, however, is called "polynomial division" and is a much more useful functionality.

@eltonlaw I think this rule should cover all x^n. However, your implementation only deals with the cases when the numerator and denominator are of the same power. It is a good place to start, but you should also consider cases like (x^3 + 4) / (x^2 + 2) where the numerator and denominator are of different exponents.

You could approach this problem in the same way you would do long division by hand. Let me know what you think of my suggestion! I'd be happy to discuss it with you.

evykassirer commented 7 years ago

sorry I seem to have caught a bad cold and also have an exam Friday morning - but later today or tomorrow I'm hoping to reply to this, sorry for the delay!

lots of interesting things to think about :)

evykassirer commented 7 years ago

okay hi!

One thing to note before going forward is that we don't have the kind of factoring set up (yet!) that we might sometimes want to cancel stuff out in complicated fractions, and that could affect this

I like the idea of starting with only x^1 (just so we get some code out there and don't write all the code all at once)

...and then expand to x^n where the top and denominator have the same exponent (similar to what @eltonlaw said - that makes sense to me, though maybe something will come up along the way we'll have to deal with)

...and then add the general case where we use the long division of polynomials approach that @aliang8 was talking about (I think that would be a good approach, though it's definitely complicate for students to think about so we should spend some time thinking about good substeps to teach it well)

By doing it incrementally, we can keep each change small and not end up on a bajillion tricky things all at once.

What do you think? :)

eltonlaw commented 7 years ago

@aliang8 Good point. Hmmmm.... Okay yeah, I agree with @evykassirer, I'll just write something for the original x^1 case so we actually have something to talk about. Uhh gimme a bit, I'll start working on this after my exam tomorrow.

evykassirer commented 7 years ago

sounds good! I'll take a look after my exam Friday ;D

eltonlaw commented 7 years ago

Hi, the solution solve the above problem, but some parts are a bit hardcoded. For example, I just did: newNumerator.push(denominator); which doesn't account for the number of times that the denominator can be found in the numerator (i.e. (4x+4)/(2x+2)) cause I didn't know how I would approach finding that. Once we get that though, it should be easy to separate it in the same way.

aliang8 commented 7 years ago

That's a great start @eltonlaw ! At this point, if you have extra time, I feel like maybe you could expand this to include x^n case. However, it would be even better if you could jump ahead and take a stab at polynomial division. That would be really helpful!

If you have any suggestions as to how to implement it, feel free to chat me up and we could bounce around some ideas. :)

evykassirer commented 7 years ago

At this point, if you have extra time, I feel like maybe you could expand this to include x^n case.

Please keep just the first case until after it goes through code review :) that way if we decide you should do something different you don't have to update everythhhhing

evykassirer commented 7 years ago

which doesn't account for the number of times that the denominator can be found in the numerator (i.e. (4x+4)/(2x+2))

Can you explain a bit more what you mean? Are you having trouble finding how to split up the numerator to match the denominator?

I think it'd be approximately this

  1. Find highest order term
  2. Compare exponents to make sure both 1 (i.e. No exponent)
  3. Compare coefficients for top and bottom
  4. Add and subtract (bottom - top) * symbol from the numerator
  5. Resolve the addition but split out the subtraction to another term

Does that seem right/make sense? Where along ​there have you gotten so far, or are you going for something different?

aliang8 commented 7 years ago

Hi everyone, I found a really cool approach to long polynomial division that I would like to share with the two of you. It's super intuitive, pedagogical, just like how we would normally do long division.

The pseudocode is here: https://rosettacode.org/wiki/Polynomial_long_division#Python

Basically, it represents the polynomials as vectors such that the i^th element keeps the coefficient of x^i e.g [-42 0 -12 1] => x^3 - 12 x^2 + 0x - 42 and multiplication by a monomial is a shift of the vector's element to the right e.g [-42 0 -12 1] * x^2 => [0 0 -42 0 -12 1] It uses a simple recursive loop to find all the coefficients of the quotient polynomial by applying the two concepts above. Highly suggest taking a look at it since it covers most if not all of the edge cases. Let me know what yall think of it 👍

eltonlaw commented 7 years ago

Hey, so sorry for going AWOL, I was just finalizing some stuff with school. @evykassirer this is how the process goes right now:

  1. Check for division between 2 nodes
  2. Check if exponents are the same (I will change this to just 1)
  3. Finds the constant value difference [Ex. (2x+3)/(2x+2) => 1] by taking args[1] and subtracting them form each other
  4. Appends denominator to the fractionList and the constant value difference
  5. Then the rest of breakUpNumerator runs

Yeah, so basically I kind of did what you recommended but I didn't calculate the symbol part, it only works for the case where you can find the denominator in the numerator once. Coefficients for the top and bottom x's weren't really looked at. I'll start working on this now.

@aliang8 Wowowow! Great find, that's actually superrrr cool. I don't know, how should we incorporate this? Any ideas?

aliang8 commented 7 years ago

@eltonlaw I'm glad you like it! I was thinking we could implement something similar to the pseudocode in the link. Theoretically, it should cover most of the edge cases in polynomial division and still be succinct and efficient. Let me know what you think and we should also confirm with @evykassirer.

Secondly, regarding your suggested approach: Great ideas! I just have a few little suggestions. In your third step, args[1] assumes that your polynomial only has two terms (3-2 right?). Maybe generalize it to include multi-term polynomials.

I don't think breakUpNumerator (step 5) is needed. The function we have right now doesn't do anything useful, so I think you can discard that step. But overall, great work!

eltonlaw commented 7 years ago

@aliang8 Yeah right now it's super fragile, it only works on 2 term cases of the form (ax^1 +b)/(cx^1 +d). I figured we could focus on the easier scenarios and discuss the methods before going into the more complicated stuff. With the breakUpNumerator step, for now I think I'm going to keep it, everything still fits okay, it's not an unnecessary constraint or anything.

Oh yeah, I just made a PR, let me know what you guys think. I added some more tests and made the checks/simplifying function a bit more robust, it's also more in tune with your specifications (I think) @evykassirer.

evykassirer commented 7 years ago

sweet! I'll take a look