Friday Puzzler - Fractions

Today's puzzler should be a rather simple one, but I'm curious to see how folks solve it. Don't forget - these Friday Puzzler's are meant to be quick tests of your ColdFusion coding skills. If you can't solve it in five minutes, than take a break. (If I get someone fired I'll feel so guilty. At least for 30 seconds.) So what's the challenge?

Given two numbers, M and N, return the fraction created by dividing M into N, but only if a 'whole' fraction is created. Otherwise return an empty string.

Examples:
(2,4) == 1/2
(3,9) == 1/3
(5,25) == 1/5

Assume, or throw an error, if the first number is larger than the second. Support from 1/1 to 1/9 for full "bonus" points. (Sorry, bonus points are not redeemable on planets with oxygen.)

Ok, get to it!

Archived Comments

Comment 1 by Dave Babbitt posted on 4/10/2009 at 5:40 PM

Does orbiting a planet count as being on that planet? ;-)

Comment 2 by Rick O posted on 4/10/2009 at 5:43 PM

function reduceFraction(a,b) {
if(a gt b) throw("omgwtf");
var c = b / a;
if(int(c) eq c) return (a/c) & "/" & (b/c);
return "";
}

Comment 3 by Raymond Camden posted on 4/10/2009 at 5:44 PM

Love the error message Rick. :)

Comment 4 by Prasanth posted on 4/10/2009 at 5:47 PM

Please see my solution. This may not be a good one.

<cfset a = 50>
<cfset b = 50>

<cfif a GT b>
<cfthrow message="a cannot be greater than b">
</cfif>

<cfif a EQ b>
<cfoutput>1</cfoutput>
<cfabort>
</cfif>

<cfif (b MOD a) EQ 0>
<cfoutput>1/#b/a#</cfoutput>
<cfelse>
<cfoutput>Not a perfect fraction</cfoutput>
</cfif>

Comment 5 by Rick O posted on 4/10/2009 at 5:49 PM

Oh blarg. Strike that entry. It doesn't do anything like what you asked.

Comment 6 by Scott Stroz posted on 4/10/2009 at 5:50 PM

<cffunction name="stupidFractionThing">
<cfargument name="m" type="numeric" required="true" />
<cfargument name="n" type="numeric" required="true" />
<cfset var ret = "" />
<cfset var check = 0 />
<cfif n EQ 0>
<cfthrow message="Silly developer, you can't divide by zero" />
</cfif>
<cfif m GT n>
<cfthrow message="You can't do that because Ray said not to allow it.">
</cfif>
<cfset check = 100/((m/n) * 100) />
<cfif check EQ int(check)>
<cfset ret = "1/"&check />
</cfif>

<cfreturn ret />
</cffunction>

Comment 7 by Peter Mattes posted on 4/10/2009 at 5:56 PM

Is that what you mean?
<cfscript>
arrVorlage=array(array("2","4"),array("3","9"),array("5","25"));
for(a=1; a lte arraylen(arrVorlage);a++){
writeoutput("(#arrVorlage[a][1]#,#arrVorlage[a][2]#)==#fractions(arrVorlage[a][1],arrVorlage[a][2])#<br>");
}
function fractions(m,n){
var f=0;
var returnvalue="0";
if(m gt n) return returnvalue;
if(n mod m neq 0) return returnvalue;
f=n/m;
return "1/#f#";

}

</cfscript>

Comment 8 by Raymond Camden posted on 4/10/2009 at 5:58 PM

@Peter: That array() function - isn't that Railo only?

Comment 9 by Rick O posted on 4/10/2009 at 6:07 PM

function factors(n) {
// return a structure of the factors of the number
var i = 2;
var f = structNew();
while(i lte n) {
if((n mod i) eq 0) {
n = n / i;
if(not structKeyExists(f,i)) f[i] = 0;
f[i] = f[i] + 1;
i = 1;
}
i = i + 1;
}
return f;
}

function combineFactors(s) {
// recombine the factors into the reduced number
var r = 1;
var k = "";
for (k in s) {
r = r * (k ^ s[k]);
}
return r;
}

function reduceFraction(a, b) {
var f = factors(a);
var g = factors(b);
var k = "";
var c = 0;
for(k in f) {
if(structKeyExists(g,k)) {
// eliminate common factors
c = min(f[k], g[k]);
f[k] = f[k] - c;
g[k] = g[k] - c;
}
}
a = combineFactors(f);
b = combineFactors(g);
return a & "/" & b;
}

Comment 10 by Ron posted on 4/10/2009 at 6:08 PM

Ray, does the function need to deal with cases like "(4,10) == 2/5" or just cases where the numerator is 1?

Comment 11 by Ron posted on 4/10/2009 at 6:10 PM

Never mind... just saw Rick's solution. He was thing the same way I was...

Comment 12 by Raymond Camden posted on 4/10/2009 at 6:11 PM

It just needs to handle cases where the numerator is 1.

Comment 13 by Cyborgirl posted on 4/10/2009 at 6:14 PM

Probably not the easiest way, but...

<cfif IsDefined("FORM.submit")>
<cfif FORM.m GT FORM.n>
<p>Error!</p>
<cfabort>
</cfif>
<cfif SQR(FORM.n) EQ FORM.m>
<cfoutput><p>1/#FORM.m#</p></cfoutput>
<cfelse>
<p>Error!</p>
</cfif>
</cfif>

(Assuming there's a form there for numbers m and n.)

Comment 14 by Peter Mattes posted on 4/10/2009 at 6:14 PM

Yes array() is railo only; pherhaps openBD,too.
But the array() has no effect for the solution.
If you need a adobe-like solution. Let it know me ;-)

Comment 15 by Chris Schofield posted on 4/10/2009 at 6:36 PM

Thought I'd give it a try:

<cffunction name="reduceFraction" access="public" returntype="string">
<cfargument name="m" required="true" type="numeric" />
<cfargument name="n" required="true" type="numeric" />

<cfset var numerator = arguments.m />
<cfset var denominator = arguments.n />
<cfset var fraction = "#numerator#/#denominator#" />
<cfset var i = 0 />
<cfset var gcd = 1 />

<!--- Throw message if numerator is greater than denominator. CMS --->
<cfif m GT n>
<cfthrow message="(m) cannot be larger than (n)." />
<cfelseif n MOD m EQ 0>
<cfset denominator = n / m />
<cfset numerator = 1 />
<cfreturn "#numerator#/#denominator#" />
</cfif>

<!--- Find the greatest common denominator. CMS --->
<cfloop from="#numerator#" to="1" index="i" step="-1">
<cfif numerator MOD i EQ 0 AND denominator MOD i EQ 0>
<cfset gcd = i />
<cfbreak />
</cfif>
</cfloop>

<cfset numerator = numerator / gcd />
<cfset denominator = denominator / gcd />

<cfreturn "#numerator#/#denominator#" />
</cffunction>

Comment 16 by jon messer posted on 4/10/2009 at 6:50 PM

function fractionalize(m,n)
{
var v = gcd(m,n);

if(m>n)
return "not!";
else
return "#m/v# / #n/v#";
}

function gcd(m,n)
{
if(m == 0)
return n;
else if(n < m)
return gcd(n,m);
else
return gcd(n mod m, m);
}

i'll admit i stole gcd from one of my own old computer science assignments

Comment 17 by Robert Gatti posted on 4/10/2009 at 6:52 PM

<cfscript>
function math(m,n) {
if(m gt n or m lt 1 or m gt 9 or n lt 0 or n gt 9)
r = 'bad input';
else if(n mod m eq 0)
r='1/#n/m#';
else
r='not whole fraction';
return r;
}

for(i = 1; i lte 9; i++)
for(j = i; j lte 9; j++)
writeoutput('m=#i#, n=#j#: #math(i,j)#<br>');
</cfscript>

Comment 18 by Barney posted on 4/10/2009 at 7:51 PM

<cfimport prefix="g" taglib="cfgroovy/tags" />
<g:script>
variables.reduce = { int n, int d ->
def factor = ((BigInteger) n).gcd(d);
return n / factor + "/" + d / factor;
}
</g:script>
#reduce.call(2, 4)#<br />
#reduce.call(3, 9)#<br />
#reduce.call(5, 25)#<br />
#reduce.call(49, 98)#<br />

Comment 19 by Raymond Camden posted on 4/10/2009 at 7:53 PM

@Barney:

Show off.

;)

Comment 20 by Barney posted on 4/10/2009 at 7:57 PM

Bah. Just because I'd rather use a method that Sun wrote and unit tests instead of my own (likely jacked) code, doesn't mean I'm a showoff. But if you prefer CFML you can do it like this:

<cfscript>
function reduce(n, d) {
var factor = createObject("java", "java.math.BigInteger").init(n).gcd(createObject("java", "java.math.BigInteger").init(d));
return n / factor & "/" & d / factor;
}
</cfscript>

Such a mess with all those createObject() calls though.

Comment 21 by Teddy R. Payne posted on 4/10/2009 at 8:40 PM

<cffunction name="fractionAsString">

<cfargument name="numerator"
type="numeric"
required="true">
<cfargument name="denominator"
type="numeric"
required="true">

<cfset var strFraction = "" />

<cfif numerator gt denominator>

<cfset strFraction = "Error: Numerator and Denominator do not create a whole fraction." />

<cfelse>

<cfif (denominator mod numerator) EQ 0>

<cfset strFraction = "1/#numerator mod denominator#" />

</cfif>

</cfif>

<cfreturn strFraction />

</cffunction>

<cfoutput>
#fractionAsString(4,16)#
</cfoutput>

Comment 22 by Teddy R. Payne posted on 4/10/2009 at 9:06 PM

10 minute solution:

<cffunction name="fractionAsString">

<cfargument name="numerator"
type="numeric"
required="true">
<cfargument name="denominator"
type="numeric"
required="true">

<cfset var strFraction = "" />
<cfset var isValid = false />

<cfif arguments.denominator eq 0>

<cfset strFraction = "Error: Divide by zero." />

<cfelseif abs(arguments.numerator) gt abs(arguments.denominator)>

<cfset strFraction = "Error: Numerator and Denominator do not create a whole fraction." />

<cfelse>

<cfif (abs(arguments.denominator) mod abs(arguments.numerator)) EQ 0>

<!--- If the fraction equals 1 --->
<cfif (abs(arguments.numerator) mod abs(arguments.denominator)) EQ 0>

<cfset strFraction = "1" />

<cfelseif abs(numerator) EQ 1>

<cfset strFraction = "1/#abs(arguments.denominator)#" />

<cfelse>

<cfset strFraction = "1/#abs(arguments.numerator) mod abs(arguments.denominator)#" />

</cfif>

<cfset isValid = true />

</cfif>

</cfif>

<!--- Add negative sign if either numerator or denominator are negative values --->
<cfif isValid AND (arguments.numerator LT 0 OR arguments.denominator LT 0)>

<cfset strFraction = "-" & strFraction />

</cfif>

<cfreturn strFraction />

</cffunction>

<cfoutput>
[#fractionAsString(6,5)#]<br />
[#fractionAsString(5,6)#]<br />
[#fractionAsString(1,5)#]<br />
[#fractionAsString(5,5)#]<br />
[#fractionAsString(-4,16)#]<br />
[#fractionAsString(0,0)#]<br />
</cfoutput>

Comment 23 by Teddy R. Payne posted on 4/10/2009 at 9:09 PM

I will stop here as I see one logic error in my code for handling negative numbers. -4/-16 = 1/4 not -1/4.

That is what I get for a 10 minute solution that needed further testing. ;)

Comment 24 by Rick O posted on 4/10/2009 at 10:01 PM

Barney-

That's awesome. I bow before you.

Comment 25 by Teddy R. Payne posted on 4/10/2009 at 11:30 PM

Rick,
Too funny =)

I looked briefly at the logic error:

<cfif isValid AND (arguments.numerator LT 0 OR arguments.denominator LT 0)>

Needs to be:

<cfif isValid AND (arguments.numerator LT 0 XOR arguments.denominator LT 0)>

A legit usage of XOR. Now that is geeky. =)