Friday Puzzler - Fractions

This post is more than 2 years old.

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!

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

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. =)