फोर्किंग फैक्टर्स


12

इस गोल्फ को एक फैक्टोरियल कैलकुलेशन की आवश्यकता होती है जिसे कई थ्रेड्स या प्रक्रियाओं के बीच विभाजित किया जाए।

कुछ भाषाएं दूसरों की तुलना में समन्वय करना आसान बनाती हैं, इसलिए यह लैंग अज्ञेयवादी है। अनगुल्ड उदाहरण कोड प्रदान किया गया है, लेकिन आपको अपना स्वयं का एल्गोरिदम विकसित करना चाहिए।

प्रतियोगिता का लक्ष्य यह देखना है कि N की गणना के लिए सबसे छोटा (बाइट्स, सेकंड में नहीं) मल्टीकोर फैक्टरोरिथम कौन आ सकता है! जब प्रतियोगिता बंद हो जाती है तो वोटों से मापा जाता है। एक मल्टीकोर लाभ होना चाहिए, इसलिए हमें आवश्यकता होगी कि यह एन ~ 10,000 के लिए काम करे। यदि लेखक प्रोसेसर / कोर के बीच काम को कैसे फैलाता है और गोल्फ की सहमति के आधार पर वोट देता है, तो लेखक को मत देने के लिए असफल होने पर मतदाताओं को वोट देना चाहिए।

जिज्ञासा के लिए, कृपया कुछ प्रदर्शन संख्याएँ पोस्ट करें। कुछ बिंदु पर एक प्रदर्शन बनाम गोल्फ स्कोर ट्रेडऑफ़ हो सकता है, जब तक यह आवश्यकताओं को पूरा करता है तब तक गोल्फ के साथ जाएं। मुझे यह जानने की उत्सुकता होगी कि ऐसा कब होता है।

आप आमतौर पर उपलब्ध एकल कोर बड़े पूर्णांक पुस्तकालयों का उपयोग कर सकते हैं। उदाहरण के लिए, पर्ल आमतौर पर बिगिन के साथ स्थापित किया जाता है। हालाँकि, ध्यान दें कि केवल सिस्टम प्रदान करने वाले फैक्टरियल फ़ंक्शन को कॉल करने से सामान्य रूप से कई कोर पर काम नहीं होगा।

आपको STDIN या ARGV से इनपुट N को स्वीकार करना होगा और N के मान को STDOUT में आउटपुट करना होगा। आप प्रोग्राम के लिए प्रोसेसर / कोर की संख्या प्रदान करने के लिए वैकल्पिक रूप से एक 2 इनपुट पैरामीटर का उपयोग कर सकते हैं, इसलिए यह वह नहीं करता है जो आप नीचे देखेंगे :-) या आप 2, 4 के लिए स्पष्ट रूप से डिज़ाइन कर सकते हैं, जो भी आपके पास उपलब्ध है।

मैं नीचे अपनी खुद की ऑडबॉल उदाहरण प्रस्तुत करूंगा, जो पहले अलग-अलग भाषाओं में फैक्टरियल एल्गोरिदम के तहत स्टैक ओवरफ्लो पर प्रस्तुत की गई थी । यह गोल्फ नहीं है। कई अन्य उदाहरण प्रस्तुत किए गए, उनमें से कई गोल्फ थे लेकिन कई नहीं। शेयर-समान लाइसेंस के कारण, ऊपर दिए गए लिंक में किसी भी उदाहरण में शुरुआती बिंदु के रूप में कोड का उपयोग करने के लिए स्वतंत्र महसूस करें।

मेरे उदाहरण में प्रदर्शन कई कारणों से कमजोर है: यह बहुत अधिक प्रक्रियाओं, बहुत अधिक स्ट्रिंग / बिगिन रूपांतरण का उपयोग करता है। जैसा कि मैंने कहा कि यह एक जानबूझकर विषम उदाहरण है। यह 5000 की गणना करेगा! यहाँ एक 4 कोर मशीन पर 10 सेकंड से कम में। हालाँकि, अगले लूप के लिए एक अधिक स्पष्ट दो लाइनर 5000 कर सकते हैं! 3.6 में चार प्रोसेसर में से एक पर।

आपको निश्चित रूप से इससे बेहतर करना होगा:

#!/usr/bin/perl -w                                                              
use strict;
use bigint;
die "usage: f.perl N (outputs N!)" unless ($ARGV[0] > 1);
print STDOUT &main::rangeProduct(1,$ARGV[0])."\n";
sub main::rangeProduct {
    my($l, $h) = @_;
    return $l    if ($l==$h);
    return $l*$h if ($l==($h-1));
    # arghhh - multiplying more than 2 numbers at a time is too much work       
    # find the midpoint and split the work up :-)                               
    my $m = int(($h+$l)/2);
    my $pid = open(my $KID, "-|");
      if ($pid){ # parent                                                       
        my $X = &main::rangeProduct($l,$m);
        my $Y = <$KID>;
        chomp($Y);
        close($KID);
        die "kid failed" unless defined $Y;
        return $X*$Y;
      } else {
        # kid                                                                   
        print STDOUT &main::rangeProduct($m+1,$h)."\n";
        exit(0);
    }
}

इसमें मेरी दिलचस्पी बस (1) ऊब को कम करने में है; और (2) कुछ नया सीखना। यह मेरे लिए कोई होमवर्क या शोध समस्या नहीं है।

सौभाग्य!


10
आप वोटों से कम से कम कोड की गणना नहीं कर सकते हैं, और गोल्फ और बहु-थ्रेड की आवश्यकता एक साथ खराब लगती है।
आआआआआआ आआआआआ आआआआआ

मेरी प्राचीन एकल कोर नोटबुक 10000 कर सकती है! पायथन में 0.2 से कम सेकंड में।
gnibbler

मल्टी-थ्रेडिंग एक सीपीयू-बाउंड प्रक्रिया लगभग हमेशा इसे धीमा कर देगी। आप जो कुछ भी कर रहे हैं वह बहुत कम या कोई प्रदर्शन लाभ नहीं है। मल्टी-थ्रेडिंग I / O-Wait के लिए है।
मेलमोकब

2
@ मेलमोकब: मैं मल्टी-कोर सिस्टम के लिए अलग-अलग भीख माँगता हूँ।
जोए

@ जोये: आह। उस छोटे से विवरण को याद किया: एस सहमत
मेलमोकब

जवाबों:


7

मेथेमेटिका

एक समानांतर-सक्षम फ़ंक्शन:

 f[n_, g_] := g[Product[N@i, {i, 1, n, 2}] Product[N@i, {i, 2, n, 2}]]

कहाँ जी है Identityया Parallelizeप्रक्रिया के प्रकार की आवश्यकता पर निर्भर करता है

समय परीक्षण के लिए, हम फ़ंक्शन को थोड़ा संशोधित करेंगे, इसलिए यह वास्तविक घड़ी समय लौटाएगा।

f[n_, g_] := First@AbsoluteTiming[g[Product[N@i,{i,1,n,2}] Product[N@i,{i,2,n,2}]]]

और हम दोनों मोड (10 ^ 5 से 9 * 10 ^ 5 तक) का परीक्षण करते हैं: (यहां केवल दो कर्नेल)

ListLinePlot[{Table[f[i, Identity],    {i, 100000, 900000, 100000}], 
              Table[f[i, Parallelize], {i, 100000, 900000, 100000}]}]   

परिणाम: यहाँ छवि विवरण दर्ज करें


क्या आप पहले कोड लाइन में एक] याद कर रहे हैं? यह असंतुलित दिखता है।
पीटर टेलर

@Peter धन्यवाद, अंतिम "]" कॉपी बफ़र के माध्यम से अपना रास्ता नहीं बना सका। सही किया।
डॉ। बेलीजरियस

1
यह सबसे छोटा प्रतीत होता है। यह भी सबसे तेज दिखता है, जब तक कि मैं कुछ गलत नहीं कर रहा हूं। मैं अब Mathematica की सदस्यता नहीं ले सकता, इसलिए सत्यापित नहीं कर सकता। भाग लेने के लिए धन्यवाद।
पॉल

7

हास्केल: 209 200 198 177 वर्ण

176 167 स्रोत + 33 10 संकलक ध्वज

यह समाधान बहुत मूर्खतापूर्ण है। यह उत्पाद को एक प्रकार के मूल्य के समानांतर में लागू करता है [[Integer]], जहां आंतरिक सूचियां अधिकतम दो वस्तुओं पर होती हैं। एक बार जब बाहरी सूची अधिकतम 2 सूचियों पर आ जाती है, तो हम इसे समतल करते हैं और सीधे उत्पाद लेते हैं। और हां, टाइप चेकर को इंटेगर के साथ कुछ एनोटेट करना होगा, अन्यथा वह संकलन नहीं करेगा।

import Control.Parallel.Strategies
s(x:y:z)=[[x,y::Integer]]++s z;s x=[x]
p=product
f n=p$concat$(until((<3).length)$s.parMap rseq p)$s[1..n]
main=interact$show.f.read

(के मध्य भाग को पढ़ने के लिए स्वतंत्र महसूस fके बीच concatऔर sके रूप में "जब तक मैं दिल लंबाई")

चीजें ऐसी लग रही थीं जैसे कि वे कंट्रोल से ParMap के बाद से बहुत अच्छे होने जा रहे हैं। समानांतर। ट्रॉफीज को कई थ्रेड्स के लिए इसे खेती करना बहुत आसान बनाता है। हालांकि, ऐसा लगता है कि जीएचसी 7 को कमांड लाइन विकल्प और एनवायरमेंट वर्जन में वास्तव में थ्रेडेड रनटाइम को मल्टीपल कोर (जिसे मैं कुल में शामिल करता था) का उपयोग करने के लिए 33 वर्णों की आवश्यकता होती है। जब तक मुझे कुछ याद नहीं आ रहा है, जो निश्चित रूप से संभव है । ( अपडेट : थ्रेडेड जीएचसी रनटाइम एन -1 थ्रेड्स का उपयोग करता दिखाई देता है, जहां एन कोर की संख्या है, इसलिए रन टाइम विकल्पों के साथ फिडेल करने की आवश्यकता नहीं है।)

संकलन करना:

ghc -threaded prog.hs

हालांकि, रनटाइम, समानांतर मूल्यांकन की हास्यास्पद संख्या को देखते हुए बहुत अच्छा था और मैंने -O2 के साथ संकलन नहीं किया। 50000 के लिए! एक दोहरे कोर मैकबुक पर, मुझे मिलता है:

SPARKS: 50020 (29020 converted, 1925 pruned)

INIT  time    0.00s  (  0.00s elapsed)
MUT   time    0.20s  (  0.19s elapsed)
GC    time    0.12s  (  0.07s elapsed)
EXIT  time    0.00s  (  0.00s elapsed)
Total time    0.31s  (  0.27s elapsed)

कुछ अलग मूल्यों के लिए कुल समय, पहला कॉलम गोल्फ के समानांतर है, दूसरा भोले अनुक्रमिक संस्करण है:

          Parallel   Sequential
 10000!      0.03s        0.04s
 50000!      0.27s        0.78s
100000!      0.74s        3.08s
500000!      7.04s       86.51s

संदर्भ के लिए, अनुभवहीन अनुक्रमिक संस्करण यह है (जिसे -O2 के साथ संकलित किया गया था):

factorial :: Integer -> Integer
factorial n = product [1..n]
main = interact $ show.factorial.read

1
IMO, आपको संकलक और दुभाषिया के लिए आर्गन की गणना करने की आवश्यकता नहीं है।
फू्रजएक्सएल

@FUxxxxl: आम तौर पर मैं सहमत होता हूं, लेकिन इस समस्या ने विशेष रूप से अनुरोध किया है कि यह कई थ्रेड्स या प्रक्रियाओं में चलता है, और उन झंडों को ऐसा करने की आवश्यकता होती है (जीएचसी 7.0.2 के साथ, नवीनतम हास्केल प्लेटफार्म से, कम से कम)।

6

रूबी - 111 + 56 = 167 वर्ण

यह एक दो फ़ाइल स्क्रिप्ट है, मुख्य फ़ाइल ( fact.rb):

c,n=*$*.map(&:to_i)
p=(0...c).map{|k|IO.popen("ruby f2.rb #{k} #{c} #{n}")}
p p.map{|l|l.read.to_i}.inject(:*)

अतिरिक्त फ़ाइल ( f2.rb):

c,h,n=*$*.map(&:to_i)
p (c*n/h+1..(c+1)*n/h).inject(:*)

बस प्रक्रियाओं की संख्या और संख्या को आर्ग्स के रूप में गणना करने के लिए लेता है और काम को उन श्रेणियों में विभाजित करता है जो प्रत्येक प्रक्रिया व्यक्तिगत रूप से गणना कर सकती है। फिर अंत में परिणामों को गुणा करता है।

यह वास्तव में दिखाता है कि रूबिनियस YARV के लिए कितना धीमा है:

Rubinius:

time ruby fact.rb 5 5000 #=> 61.84s

Ruby1.9.2:

time ruby fact.rb 5 50000 #=> 3.09s

(अतिरिक्त नोट करें 0)


1
इंजेक्शन एक प्रतीक को तर्क के रूप में ले सकता है, इसलिए आप उपयोग करके किसी चरित्र को बचा सकते हैं inject(:+)। यहाँ डॉक्स से उदाहरण है: (5..10).reduce(:+)
माइकल कोहल

@ मिचेल: धन्यवाद :)। यह भी सिर्फ इतना है कि मैं 8वहाँ था जहाँ एक होना चाहिए *था अगर किसी को भी इसे चलाने में कोई समस्या थी।
निमो 157

6

जावा, 523 519 434 430 429 चार्ट

import java.math.*;public class G extends Thread{BigInteger o,i,r=BigInteger.ONE,h;G g;G(BigInteger O,int
I,int n){o=O;i=new BigInteger(""+I);if(n>1)g=new G(O.subtract(r),I,n-1);h=n==I?i:r;start();}public void
run(){while(o.signum()>0){r=r.multiply(o);o=o.subtract(i);}try{g.join();r=r.multiply(g.r);}catch(Exception
e){}if(h==i)System.out.println(r);}public static void main(String[] args){new G(new BigInteger(args[0]),4,4);}}

अंतिम पंक्ति में दो 4s का उपयोग करने के लिए थ्रेड्स की संख्या है।

50000! निम्नलिखित ढांचे के साथ परीक्षण किया गया (मूल संस्करण का अनगुल्ड संस्करण और मुट्ठी भर कम बुरी प्रथाओं के साथ - हालांकि यह अभी भी बहुत मिल गया है) (मेरे 4-कोर लिनक्स मशीन पर) बार देता है

7685ms
2338ms
1361ms
1093ms
7724ms

ध्यान दें कि मैंने निष्पक्षता के लिए एक धागे के साथ परीक्षण को दोहराया क्योंकि जीट गर्म हो सकता है।

import java.math.*;

public class ForkingFactorials extends Thread { // Bad practice!
    private BigInteger off, inc;
    private volatile BigInteger res;

    private ForkingFactorials(int off, int inc) {
        this.off = new BigInteger(Integer.toString(off));
        this.inc = new BigInteger(Integer.toString(inc));
    }

    public void run() {
        BigInteger p = new BigInteger("1");
        while (off.signum() > 0) {
            p = p.multiply(off);
            off = off.subtract(inc);
        }
        res = p;
    }

    public static void main(String[] args) throws Exception {
        int n = Integer.parseInt(args[0]);
        System.out.println(f(n, 1));
        System.out.println(f(n, 2));
        System.out.println(f(n, 3));
        System.out.println(f(n, 4));
        System.out.println(f(n, 1));
    }

    private static BigInteger f(int n, int numThreads) throws Exception {
        long now = System.currentTimeMillis();
        ForkingFactorials[] th = new ForkingFactorials[numThreads];
        for (int i = 0; i < n && i < numThreads; i++) {
            th[i] = new ForkingFactorials(n-i, numThreads);
            th[i].start();
        }
        BigInteger f = new BigInteger("1");
        for (int i = 0; i < n && i < numThreads; i++) {
            th[i].join();
            f = f.multiply(th[i].res);
        }
        long t = System.currentTimeMillis() - now;
        System.err.println("Took " + t + "ms");
        return f;
    }
}

बड़ा संकेत के साथ जावा गोल्फिंग के लिए सही भाषा नहीं है (देखो कि मुझे क्या करना है बस मनहूस चीजों का निर्माण करना है, क्योंकि कंस्ट्रक्टर जो लंबे समय तक निजी होता है), लेकिन हे।

यह पूरी तरह से ungolfed कोड से स्पष्ट होना चाहिए कि यह कैसे काम करता है: प्रत्येक थ्रेड एक समतुल्य वर्ग modulo को थ्रेड्स की संख्या से गुणा करता है। मुख्य बिंदु यह है कि प्रत्येक थ्रेड लगभग समान रूप से काम करता है।


5

CSharp - 206 215 चार्ट

using System;using System.Numerics;using System.Threading.Tasks;class a{static void Main(){var n=int.Parse(Console.ReadLine());var r=new BigInteger(1);Parallel.For(1,n+1,i=>{lock(this)r*=i;});Console.WriteLine(r);}}

C # Parallel.For () कार्यक्षमता के साथ गणना को विभाजित करता है।

संपादित करें; ताला भूल गए

निष्पादन समय:

n = 10,000, time: 59ms.
n = 20,000, time: 50ms.
n = 30,000, time: 38ms.
n = 40,000, time: 100ms.
n = 50,000, time: 139ms.
n = 60,000, time: 164ms.
n = 70,000, time: 222ms.
n = 80,000, time: 266ms.
n = 90,000, time: 401ms.
n = 100,000, time: 424ms.
n = 110,000, time: 501ms.
n = 120,000, time: 583ms.
n = 130,000, time: 659ms.
n = 140,000, time: 832ms.
n = 150,000, time: 1143ms.
n = 160,000, time: 804ms.
n = 170,000, time: 653ms.
n = 180,000, time: 1031ms.
n = 190,000, time: 1034ms.
n = 200,000, time: 1765ms.
n = 210,000, time: 1059ms.
n = 220,000, time: 1214ms.
n = 230,000, time: 1362ms.
n = 240,000, time: 2737ms.
n = 250,000, time: 1761ms.
n = 260,000, time: 1823ms.
n = 270,000, time: 3357ms.
n = 280,000, time: 2110ms.

4

पर्ल, 140

Nमानक इनपुट से लेता है।

use bigint;$m=<>;open A,'>',
undef;$i=$p=fork&&1;$n=++$i;
{$i+=2;$n*=$i,redo if$i<=$m}
if($p){wait;seek A,0,0;$_=<A
>;print$n*$_}else{print A$n}

विशेषताएं:

  • अभिकलन विभाजन: एक तरफ विकसित होता है और दूसरे पर कुछ भी होता है (इससे अधिक जटिल कुछ भी गणना भार को संतुलित करने के लिए वर्णों की बहुत आवश्यकता होगी।
  • साझा अनाम फ़ाइल का उपयोग करते हुए IPC।

बेंचमार्क:

  • 10000! 2.3s में छपा हुआ है, 3.4s निराधार है
  • 100000! 5'08.8 में छपा है, 7'07.9 निष्कासित

4

स्काला ( 345 266 244 232 214 वर्ण)

अभिनेताओं का उपयोग करना:

object F extends App{import actors.Actor._;var(t,c,r)=(args(1).toInt,self,1:BigInt);val n=args(0).toInt min t;for(i<-0 to n-1)actor{c!(i*t/n+1 to(i+1)*t/n).product};for(i<-1 to n)receive{case m:Int=>r*=m};print(r)}

संपादित - से संदर्भ हटाए गए System.currentTimeMillis(), फैक्टर आउट a(1).toInt, से बदल दिए List.rangeगएx to y

संपादित करें 2 - whileलूप को forबदलकर, एक सूची फ़ंक्शन के लिए बाईं ओर बदल दिया गया है जो एक ही काम करता है, निहित प्रकार के रूपांतरणों पर निर्भर करता है, इसलिए 6 चार BigIntप्रकार केवल एक बार दिखाई देते हैं, प्रिंट करने के लिए बदल दिया

संपादन 3 - पता चला कि कैसे स्काला में कई घोषणाएँ करनी हैं

संपादित करें 4 - विभिन्न अनुकूलन जो मैंने सीखा है जब से मैंने पहली बार ऐसा किया है

Ungolfed संस्करण:

import actors.Actor._
object ForkingFactorials extends App
{
    var (target,caller,result)=(args(1).toInt,self,1:BigInt)
    val numthreads=args(0).toInt min target
    for(i<-0 to numthreads-1)
        actor
        {
            caller ! (i*target/numthreads+1 to(i+1)*target/numthreads+1).product
        }
    for(i<-1 to numthreads)
        receive
        {
            case m:Int=>result*=m
        }
    print(result)
}

3

स्काला- 2.9.0 170

object P extends App{
def d(n:Int,c:Int)=(for(i<-1 to c)yield(i to n by c)).par
println((BigInt(1)/: d(args(0).toInt,args(1).toInt).map(x=>(BigInt(1)/: x)(_*_)))(_*_))}

ungolfed:

object ParallelFactorials extends App
{
  def distribute (n: Int, cores: Int) = {
    val factorgroup = for (i <- 1 to cores) 
      yield (i to n by cores)
    factorgroup.par
  }

  val parallellist = distribute (args(0).toInt, args(1).toInt)

  println ((BigInt (1) /: parallellist.map (x => (BigInt(1) /: x) (_ * _)))(_ * _))

}

10 की तथ्यात्मक गणना 4 कोर उत्पन्न करके 4 कोर पर की जाती है:

  • १ ५ ९
  • २ ६ १०
  • ३ 7
  • ४ 8

जो समानांतर में गुणा किए जाते हैं। संख्याओं को वितरित करने के लिए एक सरल तरीका होगा:

 (1 to n).sliding ((n/cores), (n/cores) 
  • १ २ ३
  • ४ ५ ६
  • 9 ९
  • 10

लेकिन वितरण उतना अच्छा नहीं होगा - छोटी संख्या सभी एक ही सूची में समाप्त हो जाएगी, एक दूसरे में उच्चतम, अंतिम सूची में लंबी गणना के लिए अग्रणी (उच्च एनएस के लिए, अंतिम धागा लगभग खाली नहीं होगा , लेकिन कम से कम होते हैं (एन / कोर) -कोर तत्व।

2.9 संस्करण में स्केल में समानांतर संग्रह होते हैं, जो समानांतर आहरण को स्वयं संभालते हैं।


2

एरलंग - 295 वर्ण।

पहली बात जो मैंने कभी एर्लांग में लिखी है तो मुझे आश्चर्य नहीं होगा अगर कोई इसे आसानी से हल कर सकता है:

-module(f).
-export([m/2,f/4]).
m(N,C)->g(N,C,C,[]).
r([],B)->B;
r(A,B)->receive{F,V}->r(lists:delete(F,A),V*B)end.
s(H,L)->spawn(f,f,[self(),H,L,1]).
g(N,1,H,A)->r([s(N div H,1)|A],1);
g(N,C,H,A)->g(N,C-1,H,[s(N*C div H,N*(C-1) div H)|A]).
f(P,H,H,A)->P!{self(),A};
f(P,H,L,A)->f(P,H-1,L,A*H).

मेरी पहले की रूबी प्रविष्टि के रूप में उसी थ्रेडिंग मॉडल का उपयोग करता है: सीमा को उप-श्रेणी में विभाजित करता है और अलग-अलग थ्रेड्स में श्रेणियों को एक साथ गुणा करता है और फिर परिणाम को मुख्य धागे में गुणा करता है।

मैं यह पता लगाने में सक्षम नहीं था कि एस्केस्क्रिप्ट कैसे काम कर सकता है, इसलिए बस के रूप में सहेजें f.erlऔर erl और रन खोलें:

c(f).
f:m(NUM_TO_CALC, NUM_OF_PROCESSES).

उपयुक्त प्रतिस्थापन के साथ।

मेरे मैकबुक एयर (दोहरे कोर) पर 2 प्रक्रियाओं में 50000 के लिए 8s और 1 प्रक्रिया के लिए 10s के आसपास मिला।

ध्यान दें: यदि आपने संख्या को तथ्यात्मक रूप देने के लिए अधिक प्रक्रियाओं का प्रयास किया है, तो बस इसे जमा देता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.