Home » SQL & PL/SQL » SQL & PL/SQL » How to calculate Highest common factor in SQL
How to calculate Highest common factor in SQL [message #140835] Wed, 05 October 2005 17:34 Go to next message
rtripath
Messages: 3
Registered: October 2005
Location: USA
Junior Member
Hi

Is there any function avaialble in SQL that can return the highest common factor among a bunch of numbers. For example
10,20,25 have a highest common factor of 5.Thanks
Re: How to calculate Highest common factor in SQL [message #140896 is a reply to message #140835] Thu, 06 October 2005 01:34 Go to previous messageGo to next message
tarundua
Messages: 1080
Registered: June 2005
Location: India
Senior Member


I suppose NO , you need to write your own function to do that.

Or else read this link and if you find some then do post back.

http://www.lc.leidenuniv.nl/awcourse/oracle/server.920/a96540/toc.htm

Re: How to calculate Highest common factor in SQL [message #141015 is a reply to message #140835] Thu, 06 October 2005 11:09 Go to previous messageGo to next message
Art Metzer
Messages: 2480
Registered: December 2002
Senior Member
"A bunch"? How about "two"?
SQL> CREATE OR REPLACE FUNCTION find_gcd (
  2      p_n1    IN  POSITIVE
  3  ,   p_n2    IN  POSITIVE
  4  )
  5  RETURN POSITIVE
  6  IS
  7      l_n1    POSITIVE := p_n1;
  8      l_n2    POSITIVE := p_n2;
  9  BEGIN
 10      WHILE NOT (l_n1 = l_n2)
 11      LOOP
 12          CASE SIGN(l_n1 - l_n2)
 13          WHEN +1
 14          THEN l_n1 := l_n1 - l_n2;
 15          ELSE l_n2 := l_n2 - l_n1;
 16          END CASE;
 17      END LOOP;
 18      RETURN (l_n1);
 19  END find_gcd;
 20  /
 
Function created.
 
SQL> SELECT find_gcd(find_gcd(10,15),25) gcd
  2  FROM   DUAL
  3  /
 
       GCD
----------
         5
 
SQL>
Re: How to calculate Highest common factor in SQL [message #141017 is a reply to message #140835] Thu, 06 October 2005 11:33 Go to previous messageGo to next message
edwardstoever
Messages: 12
Registered: February 2005
Location: Pasadena, CA
Junior Member
This is one of the best questions I have seen. Mr Metzer's answer is fantastic.

Is it possible to create an aggregate function to do the same thing? I am not asking you to do it... that would be a big job. I am just curious if it can be done. Does PL/SQL lend itself to creating aggregate functions such as sum(columnn) or count(columnn)?

Thanks for that question and answer!

Edward
Re: How to calculate Highest common factor in SQL [message #141023 is a reply to message #141017] Thu, 06 October 2005 12:39 Go to previous messageGo to next message
Art Metzer
Messages: 2480
Registered: December 2002
Senior Member
Yes, it can be done, usings Oracle's user-defined aggregate functions capabilities.

Here's an example of a custom GCD aggregate function. Note, it's not production safe; anyone implementing this solution would have to determine how to handle negatives, zeroes and non-integers in their particular situation.
SQL> CREATE OR REPLACE TYPE gcd_agg AS OBJECT (
  2      factor  NUMBER
  3  ,   STATIC FUNCTION odciaggregateinitialize (
  4                          sctx IN OUT gcd_agg)
  5      RETURN NUMBER
  6  ,   MEMBER FUNCTION odciaggregateiterate (
  7                          SELF    IN OUT  gcd_agg
  8                      ,   value   IN      NUMBER)
  9      RETURN NUMBER
 10  ,   MEMBER FUNCTION odciaggregateterminate (
 11                          SELF        IN  gcd_agg
 12                      ,   returnvalue OUT NUMBER
 13                      ,   flags       IN  NUMBER)
 14      RETURN NUMBER
 15  ,   MEMBER FUNCTION odciaggregatemerge (
 16                          SELF    IN OUT  gcd_agg
 17                      ,   ctx2    IN      gcd_agg)
 18      RETURN NUMBER
 19  );
 20  /
 
Type created.
 
SQL> CREATE OR REPLACE TYPE BODY gcd_agg AS
  2      STATIC FUNCTION odciaggregateinitialize (
  3                              sctx IN OUT gcd_agg)
  4      RETURN NUMBER
  5      IS
  6      BEGIN
  7          sctx := gcd_agg(TO_NUMBER(NULL));
  8          RETURN (odciconst.success);
  9      END odciaggregateinitialize;
 10
 11      MEMBER FUNCTION odciaggregateiterate (
 12                          SELF    IN OUT  gcd_agg
 13                      ,   value   IN      NUMBER)
 14      RETURN NUMBER
 15      IS
 16          l_value         POSITIVE;
 17          l_return_value  NUMBER;
 18      BEGIN
 19          BEGIN
 20              l_value := ABS(TRUNC(value));
 21              IF (SELF.factor IS NULL
 22              OR SELF.factor <= 0) THEN
 23                  SELF.factor := l_value;
 24              ELSE
 25                  WHILE NOT (SELF.factor = l_value)
 26                  LOOP
 27                      CASE SIGN(SELF.factor - l_value)
 28                      WHEN +1
 29                      THEN SELF.factor := SELF.factor - l_value;
 30                      ELSE l_value := l_value - SELF.factor;
 31                      END CASE;
 32                  END LOOP;
 33              END IF;
 34              l_return_value := odciconst.success;
 35          EXCEPTION
 36              WHEN VALUE_ERROR THEN
 37                  l_return_value := odciconst.error;
 38          END;
 39          RETURN (l_return_value);
 40      END odciaggregateiterate;
 41
 42      MEMBER FUNCTION odciaggregateterminate (
 43                          SELF        IN  gcd_agg
 44                      ,   returnvalue OUT NUMBER
 45                      ,   flags       IN  NUMBER)
 46      RETURN NUMBER
 47      IS
 48      BEGIN
 49          returnvalue := SELF.factor;
 50          RETURN (odciconst.success);
 51      END odciaggregateterminate;
 52
 53      MEMBER FUNCTION odciaggregatemerge (
 54                          SELF    IN OUT  gcd_agg
 55                      ,   ctx2    IN      gcd_agg)
 56      RETURN NUMBER
 57      IS
 58          l_value_1   POSITIVE := SELF.factor;
 59          l_value_2   POSITIVE := ctx2.factor;
 60      BEGIN
 61          IF (l_value_1 IS NULL
 62          OR l_value_1 <= 0) THEN
 63              l_value_1 := l_value_2;
 64          ELSE
 65              WHILE NOT (l_value_1 = l_value_2)
 66              LOOP
 67                  CASE SIGN(l_value_1 - l_value_2)
 68                  WHEN +1
 69                  THEN l_value_1 := l_value_1 - l_value_2;
 70                  ELSE l_value_2 := l_value_2 - l_value_1;
 71                  END CASE;
 72              END LOOP;
 73          END IF;
 74          RETURN (odciconst.success);
 75      END odciaggregatemerge;
 76  END;
 77  /
 
Type body created.
 
SQL> CREATE OR REPLACE FUNCTION gcd (input NUMBER)
  2  RETURN NUMBER
  3  PARALLEL_ENABLE AGGREGATE USING gcd_agg;
  4  /
 
Function created.
 
SQL> CREATE TABLE t (id VARCHAR2(2), n INT)
  2  /
 
Table created.
 
SQL> INSERT INTO t VALUES ('A',25);
SQL> INSERT INTO t VALUES ('A',30);
SQL> INSERT INTO t VALUES ('A',55);
SQL> INSERT INTO t VALUES ('B',77);
SQL> INSERT INTO t VALUES ('C',7176);
SQL> INSERT INTO t VALUES ('C',5428);
SQL> INSERT INTO t VALUES ('C',7820);
 
SQL> SELECT id, gcd(t.n) FROM t GROUP BY id
  2  /
 
ID   GCD(T.N)
-- ----------
A           5
B          77
C          92
 
SQL>
Re: How to calculate Highest common factor in SQL [message #141026 is a reply to message #141015] Thu, 06 October 2005 12:47 Go to previous messageGo to next message
rtripath
Messages: 3
Registered: October 2005
Location: USA
Junior Member
Appreciate the reply. But how we can expand to do for n numbers. I can see it might be useful for others.
Re: How to calculate Highest common factor in SQL [message #141030 is a reply to message #140835] Thu, 06 October 2005 13:06 Go to previous messageGo to next message
edwardstoever
Messages: 12
Registered: February 2005
Location: Pasadena, CA
Junior Member
Well, as far as I am concerned, you have answered the question beautifully. I hope you don't mind if I post a copy of your code on my website as an example - I will give you credit - contact me here: http://www.database-expert.com/contact.asp. I have never heard of user-defined aggregate functions, but the thought has crossed my mind from time to time, so this is welcome "news".

Regarding using his function for n numbers (an unknown quantity of numbers), you can use it to get the results of two numbers and compare the results with a third number, which could then be done again for a fourth, fifth, sixth, and so on. Modifying it to handle a table of numbers should not be too difficult.

Well done!

Edward
Cool
Re: How to calculate Highest common factor in SQL [message #502856 is a reply to message #141030] Mon, 11 April 2011 00:18 Go to previous messageGo to next message
rleishman
Messages: 3728
Registered: October 2005
Location: Melbourne, Australia
Senior Member
I know it's an old thread, but...
I have an improvement on this function that uses Euclid's algorithm for finding the greatest common factor/divisor. It is considerably faster than the one posted above.

create or replace 
type gcf_type as object 
( 
  num integer,
  static function ODCIAggregateInitialize 
    ( sctx in out gcf_type ) 
    return number ,
  member function ODCIAggregateIterate 
    ( self  in out gcf_type , 
      value in     integer 
    ) return number ,
  member function ODCIAggregateTerminate 
    ( self        in  gcf_type, 
      returnvalue out integer, 
      flags in number 
    ) return number ,
  member function ODCIAggregateMerge 
    ( self in out gcf_type, 
      ctx2 in     gcf_type 
    ) return number 
); 
/


create or replace 
type body gcf_type 
is
  static function ODCIAggregateInitialize 
  ( sctx in out gcf_type ) 
  return number 
  is 
  begin 
    sctx := gcf_type( null ) ; 
    return ODCIConst.Success ; 
  end;
  member function ODCIAggregateIterate 
  ( self  in out gcf_type , 
    value in     integer 
  ) return number 
  is 
     big integer; 
     sml integer; 
     rem integer; 
     val number; 
  begin 
    CASE 
        WHEN self.num IS NULL  OR self.num = 0   THEN self.num := abs(value); 
        WHEN value IS NULL     OR value = 0      THEN NULL; 
        WHEN abs(self.num) = 1 OR abs(value) = 1 THEN self.num := 1; 
        WHEN abs(self.num) = abs(value)          THEN NULL; 
        ELSE 
            big := greatest(abs(self.num), abs(value)); 
            sml := least(abs(self.num), abs(value));           
            self.num := sml;           
            rem := mod(big, sml); 
            IF rem > 0 THEN 
                val := self.ODCIAggregateIterate(rem); 
            END IF; 
    END CASE; 
    
    return ODCIConst.Success; 
  end;
  member function ODCIAggregateTerminate 
  ( self        in  gcf_type , 
    returnvalue out integer , 
    flags       in  number 
  ) return number 
  is 
  begin 
    returnValue := self.num; 
    return ODCIConst.Success; 
  end;
  member function ODCIAggregateMerge 
  ( self in out gcf_type , 
    ctx2 in     gcf_type 
  ) return number 
  is 
      val number; 
  begin 
    val := self.ODCIAggregateIterate(ctx2.num); 
    return ODCIConst.Success; 
  end;
end; 
/


create or replace 
function gcf 
  ( input integer ) 
  return integer 
  deterministic 
  parallel_enable 
  aggregate using gcf_type 
; 
/


Ross Leishman
Re: How to calculate Highest common factor in SQL [message #502926 is a reply to message #141017] Mon, 11 April 2011 11:32 Go to previous messageGo to next message
Solomon Yakobson
Messages: 3273
Registered: January 2010
Location: Connecticut, USA
Senior Member
edwardstoever wrote on Thu, 06 October 2005 12:33
This is one of the best questions I have seen. Mr Metzer's answer is fantastic.


No offense, but GCD is school material. Method to find GCD is called Euclidean Algorithm (it was first described by the famous Greek mathematician Euclid) and is 23 hundred years old.

SY.

[Updated on: Mon, 11 April 2011 11:36]

Report message to a moderator

Re: How to calculate Highest common factor in SQL [message #502965 is a reply to message #502926] Mon, 11 April 2011 19:36 Go to previous message
rleishman
Messages: 3728
Registered: October 2005
Location: Melbourne, Australia
Senior Member
I'm sure Mr Stover has taken no offense, particularly since he has not logged into this site for the last 6 years.

And I did credit Euclid in my post, so I don't think he will be offended either Wink
Previous Topic: PL/SQL Date Error:
Next Topic: Identifying column name based on a value
Goto Forum:
  


Current Time: Fri Apr 26 04:31:29 CDT 2024