Jump to content

Some math udf's


Uten
 Share

Recommended Posts

Autoit is rather lame (slow) for this kind of algorithms. And my implementation is probably (certainly) not optimized. The C versions are like 1000 times faster, so now you are warned..;)

Func GetProperPositiveDivisors($num)
    Local $a[10]
    $a[0] = 0
    For $i = 1 to Floor($num/2)
        If mod($num, $i) = 0 Then 
            $a[0] += 1
            If $a[0] > Ubound($a) -1 Then ReDim $a[$a[0] + 10]
            $a[$a[0]] = $i
        EndIf
    next
    $a[0] += 1
    If $a[0] > Ubound($a) -1 Then ReDim $a[$a[0] + 10]
    $a[$a[0]] = $num
    ReDim $a[$a[0] + 1]
    Return $a
EndFunc
Func IsPerfectNumber($num)
    Local $res
    Local $a = GetProperPositiveDivisors($num)
    For $i = 1 to $a[0]
        If $a[$i] < $num Then $res += $a[$i]
    next
    ;ConsoleWrite("IsPerfectNumber[$num:=" & $num & ", $res:=" & $res & "]:=" & ($num = $res) & @crlf)
    return ($num = $res)
EndFunc
func factorsDump(ByRef $a, $head="", $tail= "")
    if $head = "" Then 
        ConsoleWrite("Factors( count:=" & $a[0] & ") : ")
    Else
        ConsoleWrite($head)
    EndIf
    for $i = 1 to $a[0]
        ConsoleWrite($a[$i] & ", ")
    Next
    ConsoleWrite(@CRLF)
EndFunc

func GetFactors($num)
    ; Return an array with all factors of a given number.
    ; NOTE: This function will fail (not return the smallest factors) for 
    ; numbers that is dividable by primes bigger than 997.
    ; To solve that we could increase the prime table or let the function 
    ; calculate the primes needed if it reache a undetermined situation
    ;SOURCE PRIMES: http://en.wikipedia.org/wiki/List_of_prime_numbers#The_first_500_prime_numbers
    Local $primes = "2 ,3 ,5, 7 ,11 ,13 ,17 ,19 ,23 ,29 ," & _
    "31 ,37 ,41 ,43 ,47 ,53 ,59 ,61 ,67 ,71 ," & _
    "73 ,79 ,83 ,89 ,97 ,101 ,103 ,107 ,109 ,113 ," & _
    "127 ,131 ,137 ,139 ,149 ,151 ,157 ,163 ,167 ,173 ," & _
    "179 ,181 ,191 ,193 ,197 ,199 ,211 ,223 ,227 ,229 ," & _
    "233 ,239 ,241 ,251 ,257 ,263 ,269 ,271 ,277 ,281 ," & _
    "283 ,293 ,307 ,311 ,313 ,317 ,331 ,337 ,347 ,349 ," & _
    "353 ,359 ,367 ,373 ,379 ,383 ,389 ,397 ,401 ,409 ," & _
    "419 ,421 ,431 ,433 ,439 ,443 ,449 ,457 ,461 ,463 ," & _
    "467 ,479 ,487 ,491 ,499 ,503 ,509 ,521 ,523 ,541 ," & _
    "547 ,557 ,563 ,569 ,571 ,577 ,587 ,593 ,599 ,601 ," & _
    "607 ,613 ,617 ,619 ,631 ,641 ,643 ,647 ,653 ,659 ," & _
    "661 ,673 ,677 ,683 ,691 ,701 ,709 ,719 ,727 ,733 ," & _
    "739 ,743 ,751 ,757 ,761 ,769 ,773 ,787 ,797 ,809 ," & _
    "811 ,821 ,823 ,827 ,829 ,839 ,853 ,857 ,859 ,863 ," & _
    "877 ,881 ,883 ,887 ,907 ,911 ,919 ,929 ,937 ,941 ," & _
    "947 ,953 ,967 ,971 ,977 ,983 ,991 ,997"
    $primes = StringSplit($primes, " ,")

    ;Local $primes = StringSplit("2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113",",")
    ; Calculate the factors in $num
    Local $size = 10
    Local $factors[$size]
    $factors[0] = 0
    Local $pos = 1

    While $pos <=$primes[0] AND $num > 1
        If mod($num, $primes[$pos]) = 0 Then 
            $factors[0] += 1
            If $size -1 < $factors[0] Then ReDim $factors[$factors[0] + 10]
            $factors[$factors[0]] = $primes[$pos]
            $num = $num/$primes[$pos]
            ; Do not change $pos as we might want the same prime again!
        Else
            $pos += 1
        EndIf
    WEnd
    If $num > 1 then 
            $factors[0] += 1
            If $size -1 < $factors[0] Then ReDim $factors[$factors[0] + 10]
            $factors[$factors[0]] = $num
    EndIf
    Return $factors
endfunc

EDIT: Something funny was inserted at the bottom of my code. Looked like binary stuff? Thanks to nahuel for pointing it out..:P

Edited by Uten
Link to comment
Share on other sites

Cool, but the forum messed up your post :P

I made some math UDF's too. I never shared because I think they are not really useful. Still, fun making.

;============
;Returns the n-th Fibonacci number
;Author: Nahuel
;============
Func Fibonacci($n)
    If Not IsInt($n) Then Return -1
    If $n < 0 Then Return -1
    If $n < 2 Then
        Return $n
    Else
        Return (Fibonacci($n-1) + Fibonacci($n-2))
    EndIf
EndFunc

;=============
;Returns the n-th triangular number
;Author: Nahuel
;=============
Func Triangular($n)
    If Not IsInt($n) Then Return -1
    If $n < 0 Then Return -1
    If $n > 0 then
       $tri = Triangular( $n- 1) + $n
    else
       $tri=0
   EndIf
   Return $tri
EndFunc

;=============
;Returns the factorial of a non-negative integer (up to 170)
;Author: Nahuel
;=============
Func Factorial($n)
    ;Check parameters
    If Not IsInt($n) Then Return -1
    If $n < 0 Then Return -1
    If $n >170 Then Return -1
    ;Do the math
    If $n <=20 Then
        Local $a=1
        For $i=1 To $n
            $a = $a*$i
        Next
    Else
        Local $a=0.1e1
        For $i=0.1e1 To $n
            $a = $a*$i
        Next
    EndIf
    Return $a
EndFunc
Link to comment
Share on other sites

@Nahuel. Thanks for the compliment and pointing out the funny stuff in the post. Wonder where that came from?

Link to comment
Share on other sites

Cool, but the forum messed up your post ;)

I made some math UDF's too. I never shared because I think they are not really useful. Still, fun making.

*snip*oÝ÷ Ûú®¢×­çÚrÚ+yÛazÇ­ê-êÝk&Ú±çhÊ&yÊxÂÚòx-¢·¹Ç­æ}ç-¶­­ç.®È¯yÆ¥Ì(ºWaj÷¨«2²×¦jw_¢éݶ¬yû§rبËhÂäªè­{¥.­:âjx.ªÚÑbnÚqȲ©eË
.Û%£hÂ|"¶î·«±¦è½ë˹ëhjYmêÞrêì÷jYlÜ(ºWaj÷­¢f¤xm­é·«¶Ø«z¶§¶ØZ´ÞiÜ"¶azaz·(uæî¶+pjËܨ¹Ê.Ö¥-jÊÊÞ}§-¢·(uëhgyçm¡Ú"µ©ÝÂ+a¶¬yû§rب«yÚ.¶0b«§-¢¸¬";¬¶ëu§b}÷«z{lÊ{ZÆØZ¶+,(!¶jË^­»­²Ø¥è¾'^²Ø^rëyËkzË¥¶Æ®¶­sdgVæ2f&öæ66b33c¶â¢FÒb33c¶'&³5ÒÒ³ÃÃТbæ÷B4çBb33c¶âFVâ&WGW&âÓ¢bb33c¶âfÇC²FVâ&WGW&âÓ¢bb33c¶âfÇC²"FVâ&WGW&âb33c¶à¢f÷"b33c¶ÒFòb33c¶à b33c¶'&³ÒÒb33c¶'&³Ò²b33c¶'&³%Ð b33c¶'&³%ÒÒb33c¶'&³Ð b33c¶'&³ÒÒb33c¶'&³Ð¢æW@¢&WGW&âb33c¶'&³Ð¤VæDgVæ0 ¤gVæ2G&æwVÆ"b33c¶â¢FÒb33c·G&Ò¢bæ÷B4çBb33c¶âFVâ&WGW&âÓ¢bb33c¶âfÇC²FVâ&WGW&âÓ¢bb33c¶âÒFVâ&WGW&â¢f÷"b33c¶ÒFòb33c¶à b33c·G&³Òb33c¶¢æW@¢&WGW&âb33c·G&¤VæDgVæ0 ¤gVæ2f7F÷&Âb33c¶â¢´6V6²&ÖWFW'0¢bæ÷B4çBb33c¶âFVâ&WGW&âÓ¢bb33c¶âfÇC²FVâ&WGW&âÓ¢bb33c¶âfwC³sFVâ&WGW&âÓ¢´FòFRÖF¢bb33c¶âfÇC³Ó#FVà Æö6Âb33c¶Ó f÷"b33c¶ÓFòb33c¶à b33c¶£Òb33c¶ æW@¢VÇ6P Æö6Âb33c¶ÓãS f÷"b33c¶ÓãSFòb33c¶à b33c¶£Òb33c¶ æW@¢VæD`¢&WGW&âb33c¶¤VæDgVæ

I might have a go at the other stuff I've seen, I like doing stuff like this. :P

Link to comment
Share on other sites

$Timer=TimerInit()
Fibonacci(150)
$Diff=TimerDiff($Timer)
ConsoleWrite("Skinny's Fibonacci(): " & $Diff & @CR)

$Timer=TimerInit()
_Fibonacci(150)
$Diff=TimerDiff($Timer)
ConsoleWrite("Nahuel's Fibonacci(): " & $Diff & @CR)

$Timer=TimerInit()
Triangular(150)
$Diff=TimerDiff($Timer)
ConsoleWrite("Skinny's Triangular(): " & $Diff & @CR)

$Timer=TimerInit()
_Triangular(150)
$Diff=TimerDiff($Timer)
ConsoleWrite("Nahuel's Triangular(): " & $Diff & @CR)

Func Fibonacci($n)
   Dim $array[3] = [0,0,1]
   If Not IsInt($n) Then Return -1
   If $n < 0 Then Return -1
   If $n < 2 Then Return $n
   For $i = 1 To $n
      $array[0] = $array[1] + $array[2]
      $array[2] = $array[1]
      $array[1] = $array[0]
   Next
   Return $array[0]
EndFunc

Func _Fibonacci($n)
    If Not IsInt($n) Then Return -1
    If $n < 0 Then Return -1
    If $n < 2 Then
        Return $n
    Else
        Return (Fibonacci($n-1) + Fibonacci($n-2))
    EndIf
EndFunc

Func _Triangular($n)
    If Not IsInt($n) Then Return -1
    If $n < 0 Then Return -1
    If $n > 0 then
       $tri = Triangular( $n- 1) + $n
    else
       $tri=0
   EndIf
   Return $tri
EndFunc

Func Triangular($n)
   Dim $tri = 0
   If Not IsInt($n) Then Return -1
   If $n < 0 Then Return -1
   If $n = 0 Then Return 0
   For $i = 1 To $n
      $tri += $i
   Next
   Return $tri
EndFunc

Wow, there sure is a big difference! Specially with the Fibonacci one. I don't know if it's my slow computer or what, but the both triangular udf's seem to be pretty even...

Thanks for the corrections :P

Link to comment
Share on other sites

Looking for refactoring possibilities is a really good habit @SkinyWhiteGuy. You get cookie points for that...;)

Recursion can be cool, but should be avoided if possible. But as in @Nahuel's Fibonacci sample, recursive code makes readable and good looking code.

Now, about speed. AutoIt is not a good language for algorithms. It is, probably the best, Windows "glue" language. But it really sucks when it comes down to lengthy computations. I did a profiling test with AutoIt, thinbasic, freebasic, EBasic, hotBasic, Aurora, gcc and lcc. The math test was based on 500.000 iterations of a routine doing trigonometric calculations. In this test the AutoIt executable was so slow that I could not let it finish the task! :P

In my string comparing test (also 500.000 iterations) AutoIt came out 100 times slower than the fastest solution.

So lesson learned. Use Autoit as a front end to a dll created with something else. lcc came out fastest in my test. After I did the test I became aware of tcc (tiny c compiler) by Fabrice Bellard. Now there you have a perfect AutoIt C companion. The installation is only a few MB, and you get all the raw power of C. Only be aware that you have to specify the Include and lib path on the commandline when you use the compiler on Windows.

Thanks for the contributions so fare..:)

Happy Scripting

Uten

Link to comment
Share on other sites

Uten

I would like to dive into your idea of tcc as AutoIt C companion but I don't succed in making a dll.

Could you, please, provide further explanations for this step an the following next : how to call the dll ?

Thanks

Patrick

Link to comment
Share on other sites

Uten

I would like to dive into your idea of tcc as AutoIt C companion but I don't succed in making a dll.

Could you, please, provide further explanations for this step an the following next : how to call the dll ?

Thanks

Patrick

Well, I figured this out with Dev C++ awhile back, here is a link to that post:

Understanding dll files, and how to make them

I'm trying to see how tcc might be integrated into Dev-C++, but since gcc is provided with it, I'm not in that big a hurry.

@Uten

I tend to use AutoIt as a profiling language for different algorithms and testing to see how fast I can get things. I can comfortably code things in AutoIt and not worry about variable types or a lot of other things that tend to slow me down in terms of making a program.

Oh, and I'll take those cookie points, too. :P

Link to comment
Share on other sites

To compile the tcc samples I have made a cmd file in my path (this is the on called later when I call tcc).

tcc.cmd

c:\devtools\tcc-0.9.23\tcc\tcc.exe -Ic:\devtools\tcc-0.9.23\include -Ic:\devtools\tcc-0.9.23\include\winapi -Lc:\devtools\tcc-0.9.23\lib %*

Observe that the paths are absolute and without spaces in them.

To compile the dll sample in tcc's examples folder

cd c:\devtools\tcc-0.9.23\
tcc -luser32 -shared examples\dll.c

Create the def file

tcc\tiny_impdef.exe dll.dll

And the dll test application

tcc examples\hello_dll.c dll.def

And finally the a AutoIt test script.

DllCall("dll.dll","none", "HelloWorld")

the au3 file must be in the same folder as the dll.dll. In my case that is c:\devtools\tcc-0.9.23. Or dll.dll must be in a folder defined in the PATH environment variable.

Happy scripting

Uten

Link to comment
Share on other sites

Thanks a lot Uten! I'm gonna try that ;) Although I'm having some trouble with the cmd lines ¬¬

So, I'm using Dev-C++ at the moment. I wrote the Fibonacci function in a DLL, here's the code:

#define EXPORT extern "C" __declspec(dllexport) __stdcall

// DLL initialization
BOOL APIENTRY
DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
{
        return TRUE;
}

// Defined Functions to be available to other processes

EXPORT int Fibonacci(int n)
{
       if (n < 0) {
            return 0;
            }
       if (n < 2) {
            return n;
            }
       else {
            return (Fibonacci(n-1) + Fibonacci(n-2));
            }
}

I compiled it and it works pretty well

$answer = DllCall ( "math.dll", "int", "Fibonacci", "int", 10)
MsgBox(0, @error, $answer[0])oÝ÷ Ø­×hzÉ÷öÜ(®F޶׫¶§JH§!®Ël&è§)ÀºÚ´¢jezץʧÌ"¶î·«±¸ êí©÷Ð'£­ßÛyÆ®±âyÉZ­çbø±¸§·öÁºØ"ëජjbêìk+q©÷ö×(«y©ÈÜ"¶¥¢xØbem²Øb²X§z([vþ$¨"Ë©¦#e¯zÚ ¡©Ýæ«'Ð%²!ÉÚ²+kzÛ«©Ú®¶²jëh×6MsgBox(0, @error, $answer[0])

:P

Edited by Nahuel
Link to comment
Share on other sites

Looking forward to see all the creative spinnoffs (hmm, don't you say that. At least not in ASpell?) this will trigger..;)

@Nahuel: Some of the stuff you have done, when your bored, are grate. So, yes you should definitely spend some time learning basic C. And make the commandline your friend. Take a look in my blog. And search for "cmd here".

NOTE: Do not think C++ supports C anymore (as of C99). Try to make a distinction between your C and your C++ code (A really good reason to use tcc..:P (or a LCC based setup))

Link to comment
Share on other sites

Thanks a lot Uten! I managed to use Notepadd ++ to edit and TCC to compile :P It works, but it's extremely slow. I guess I'll try to avoid using recursive calls... I've never programed in C before, so this is hard as hell ;)

@SkinnyGuy: I replaced int by long and it works. But it's like 10000 slower than the one you did!

Link to comment
Share on other sites

Lol, well, the Fibonacci sequence is hard on a computer when done like that. Just to look up 1 number, it has to recall the same function 2 times, and if that number isn't one of our other default cases, it also has to run 2 more function calls. Kinda the you tell 2 friends who tell 2 friends each, who then tell 2 friends each, etc... thing. Add to that, that it ends up recalculating numbers it already knew from before (Fib(8) = Fib(7) + Fib(6), Fib(7) = Fib(6) + Fib(5), etc...) and it's not very efficient at all.

Link to comment
Share on other sites

Yes, so true ;) I don't think it's worth it trying to build a DLL for functions like this one, when the one you did in AutoIt works just great. It should make a difference with Uten's udf's though :)

Since you seem to know the Fibonacci serie pretty well, I can't figure out how to make a function recognize if a given number is in the Fibonacci serie.. there is an equation, but it's kinda hard to make it with AutoIt :P

Link to comment
Share on other sites

You mean this equation:

Func _Fib($num)
    Return Int((((1+Sqrt(5))^$num) - ((1-Sqrt(5))^$num)) / ((2^$num)*Sqrt(5)))
EndFuncoÝ÷ ØdwZël¡÷çyªíç§uÛ¶^r)Ƨu©e¶­"¯z}ýµÚ'yéèºpØb¶Ú$!£hrëyË_¢»ajÖî¶'ò¢êÞÞ¶×ë¢h­jw¢jZ­è­Â+a¶¨¶«ëa¡Û-+¢¹®¬"WË¥j×­ábnÚqȧºfÞ®Þ¶ÜqË«j×¥Èj¶¬r)àj{¦mêí¢Çø­ßÛ"Ø^±êâzÂ7õ×hÜ"¶¥¢l¢g­)à)jëh×6While $number_to_test < Fibonacci($n)
    $n+=1
Wend
If $number_to_test <> Fibonacci($n) Then Return False
Return True

Of course, that's just off the top of my head.

Edit: Just checked, the _Fib function above starts getting off for values above 70, and I couldn't think of a way to make it stop doing whatever it's doing, so I'd suggest either not using it, or limiting the input to values below 70.

Edited by SkinnyWhiteGuy
Link to comment
Share on other sites

Here is my spinoff of the GetFactors function.

This will return a 2 dimensional array with ALL factors in column 1, and true in column 2 if its prime.

No preset group of primes is required. I am comparing my results to this page: http://www.apples4theteacher.com/prime-factors.html

#include <array.au3>
_ArrayDisplay(GetFactorsX(12345))

Func GetFactorsX($num)
    Local $factorArray[1][2]
    $count = 1
    
    ;Loop through every number from 1 to requested number
    For $X = 1 to $num
        If Mod($num, $X) = 0 Then
            Redim $factorArray[$count][2]
            $factorArray[$count-1][0] = $X
            $factorArray[$count-1][1] = true
            
            ;Check if prime
            For $Y = 2 to $X - 1
                ;If the number can be divided by anything other than 1 or itself
                If Mod($X, $Y) = 0 AND $X > 1 Then
                    $factorArray[$count-1][1] = false
                    ExitLoop
                EndIf
            Next

            $count += 1
        EndIf
    Next
    
    Return $factorArray
EndFunc
Edited by weaponx
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...