Neden && 75 kat daha if… fi'den ve kodun daha açık hale getirildiğinden daha hızlı kullanılıyor?


38

Aşağıdaki çalışma koduna sahibim:

largest_prime=1
for number_under_test in {1..100}
do
  is_prime=true
  factors=''
  for ((divider = 2; divider < number_under_test-1; divider++));
  do
    remainder=$(($number_under_test % $divider))
    [ $remainder == 0 ] && [ is_prime ] && is_prime=false && factors+=$divider' '
  done
  [ $is_prime == true ] && echo "${number_under_test} is prime!" || echo "${number_under_test} is NOT prime (factors= $factors)"  [ $is_prime == true ] && largest_prime=$number_under_test
done
printf "\nLargest Prime= $largest_prime\n"

Bu kod hızlı bir şekilde çalışıyorsa 0.194 saniyedir. Ancak && is_prime= falsebiraz okumayı zor buldum ve ne yaptığını ayarlamak yerine test ediliyormuş gibi (eğitimsiz göze) bakabiliyordu. Değiştim çalıştı Yani &&bir içine if...then14.48 saniyede 75 kat daha yavaş ama - ve bu eserlerin. En yüksek rakamlarda en belirgindir.

largest_prime=1
for number_under_test in {1..100}
do
  is_prime=true
  factors=''
  for ((divider = 2; divider < number_under_test-1; divider++));
  do  
    remainder=$(($number_under_test % $divider))
    if ([ $remainder == 0 ] && [ $is_prime == true ]); then
      is_prime=false
      factors+=$divider' '
    fi  
  done
  [ $is_prime == true ] && echo "${number_under_test} is prime!" || echo "${number_under_test} is NOT prime (factors= $factors)"  [ $is_prime == true ] && largest_prime=$number_under_test
done  
printf "\nLargest Prime= $largest_prime\n"

Herhangi bir yavaşlık olmadan bloğun netliğini sağlamak oldu mu?

Güncelleme (1/4/2015 10:40 EST)

Harika geri bildirim! Şimdi aşağıdakileri kullanıyorum. Başka geribildirim var mı?

largest_prime=1
separator=' '
for number_under_test in {1..100}; {
  is_prime=true
  factors=''
  for ((divider = 2; divider < (number_under_test/2)+1; divider++)) {
    remainder=$(($number_under_test % $divider))
    if [ $remainder == 0 ]; then
      is_prime=false
      factors+=$divider' '
    fi
  } 
  if $is_prime; then
    printf "\n${number_under_test} IS prime\n\n"
    largest_prime=$number_under_test
  else
    printf "${number_under_test} is NOT prime, factors are: "
    printf "$factors\n"
  fi
}
printf "\nLargest Prime= $largest_prime\n"

1
Bir komut satırında, betiğinizi çalıştırmak Largest Prime= 100bilgisayarıma yazdırır .
Giulio Muscarello

3
Ayrıca sidenote üzerinde size verimlilik ilgilenen eğer, tek önemsiz yolu bu sadece iterate kadar olacaktır geliştirmek için number_under_test/2yerine yukarı kadar olan number_under_test-1bir dizi yok faktörünü n hala tüm bulacaksınız bu yüzden, n / 2'den büyük: asal olmayan sayılar için faktörleri bunu yaparak (Ayrıca, yalnızca asallık için test yapmakla ilgileniyorsanız, sqrt (n) 'e kadar yinelemek yeterli olacaktır, ancak Bash'in yine de kare kökleri hesaplamak için yerleşik bir işlevi yoktur.)
Malte Skoruppa

Mat, iyi nokta (+1). Tek değişiklik, bu 4 numara için işe yaramadı, ben de buna (number_under_test/2)+1izin vermem gerekiyordu
Michael Durrant,

1
Güncellenen versiyonda, parantez {}gerçekten sonra gerekli değildir thençünkü maddesi thenzaten (birlikte bir gruplandırma operatörü olarak hizmet veren elif, elseya da fi). Aslında, bazı kabuklarda, sen, örneğin, yazabiliriz for i in 1 2 3; { echo $i; }hiçbir ile doveya done.
Jonathan Leffler,

1
+1 Jonathan, bu değişiklikleri yaptım ve güncellemeyi güncelledim
Michael Durrant

Yanıtlar:


66

Çünkü her seferinde bir deniz kabuğu döküyorsun:

if ([ $remainder == 0 ] && [ $is_prime == true ]); then

Sadece parantezleri kaldır

if [ $remainder == 0 ] && [ $is_prime == true ]; then

Komutları gruplamak istiyorsanız, geçerli kabuğunda bunu yapmak için sözdizimi vardır :

if { [ $remainder == 0 ] && [ $is_prime == true ]; }; then

(son noktalı virgül gereklidir, kılavuza bakın )

Bunun [ is_prime ]aynı olmadığını unutmayın [ $is_prime == true ]: bunu $is_primebash yerleşikini trueveya falsekomutunu çağıracak şekilde basitçe (parantezsiz) yazabilirsiniz .
[ is_prime ]bir argüman olan bir sınama, "is_prime" dizesi - [tek bir argüman verildiğinde, argüman boş değilse ve sonuç dizgesi her zaman boş olmazsa sonuç başarılı olur, dolayısıyla her zaman "true" olur.

Okunabilirlik için çok uzun bir çizgiyi değiştiririm

[ $is_prime == true ] && echo "${number_under_test} is prime!" || echo "${number_under_test} is NOT prime (factors= $factors)"  [ $is_prime == true ] && largest_prime=$number_under_test

için

if [ $is_prime == true ]; then
  echo "${number_under_test} is prime!"
else 
  echo "${number_under_test} is NOT prime (factors= $factors)"
  # removed extraneous [ $is_prime == true ] test that you probably
  # didn't notice off the edge of the screen
  largest_prime=$number_under_test
fi

Netliği arttırmak için boşlukları küçümsemeyin.


1
Bir largest_prime=$number_under_test
Yazım

1
Ayrıca, bash, zsh, et al, [kelimenin tam anlamıyla denilen bir programı çağırıyor [, oysa [[kabuk içinde uygulanıyor - bu nedenle daha hızlı olacak. Deneyin time for ((i = 0; $i < 1000; i++)); do [ 1 ]; doneve karşılaştırın [[. Daha fazla bilgi için bu SO soruya bakınız .
kirb

2
bash uygular [, bu bir yerleşiktir. Bir kabuk bilgi isteminde şunu yazın type -a [vehelp [
glenn jackman

@glennjackman Wow; bunun farkında değildi. which [Hala geri döndüğü için hala böyle olduğunu varsaydım /usr/bin/[. Ayrıca, zsh'nin aynı olduğunu ima ettiğimi farkettim; bana göre bu bir yerleşik olduğunu söylüyor. Ama sonra ... neden [[daha hızlı?
kirb

2
@glennjackman command -vbir başka iyi whichalternatiftir; buraya da bakınız .
Abbafei

9

Bence sen bu fonksiyon için fazla çalışıyorsun. Düşünmek:

unset num div lprime; set -- "$((lprime=(num=(div=1))))"
while [     "$((     num += ! ( div *= ( div <= num   ) ) ))" -eq \
            "$((     num *=   ( div += 1 )   <= 101   ))" ]    && {
      set   "$(( ! ( num %      div )         * div   ))"     "$@"
      shift "$(( !    $1 +    ( $1 ==  1 )    *  $#   ))"
}; do [ "$div" -gt "$num" ] && echo "$*"      
done

Shell aritmetiği, tamsayılı koşulları kendi başına değerlendirebilir. Nadiren çok fazla teste ve / veya dış görevlendirmeye ihtiyaç duyar. Bu bir whiledöngü iç içe döngülerinizi oldukça iyi çoğaltır:

Tabii ki o kadar da yazmıyor, elbette o kadar fazla yazmadım, ama örneğin tavanı yukarıda yazılı olan 101 yerine 16'ya ayarlamak ve ...

2
3
4 2
5
6 3 2
7
8 4 2
9 3
10 5 2
11
12 6 4 3 2
13
14 7 2
15 5 3

Kesinlikle işi yapıyor. Ve çıktınızı tahmin etmek için çok az başka bir şey gerektirir:

...
do [ "$div" -eq "$num" ] && shift &&
   printf "$num ${1+!}= prime.${1+\t%s\t%s}\n" \
          "factors= $*"                        \
          "lprime=$(( lprime = $# ? lprime : num ))"
done

Sadece bunu yapmak yerine echo...

1 = prime.
2 = prime.
3 = prime.
4 != prime.     factors= 2      lprime=3
5 = prime.
6 != prime.     factors= 3 2    lprime=5
7 = prime.
8 != prime.     factors= 4 2    lprime=7
9 != prime.     factors= 3      lprime=7
10 != prime.    factors= 5 2    lprime=7
11 = prime.
12 != prime.    factors= 6 4 3 2        lprime=11
13 = prime.
14 != prime.    factors= 7 2    lprime=13
15 != prime.    factors= 5 3    lprime=13

Bu çalışır busybox. Çok taşınabilir, hızlı ve kullanımı kolaydır.

Kişisel altkabuk sorun büyük kabukları meydana gidiyor, ama onu olduğu kadar bir de, çoğu akut bashkabuğu. Yapma arasında geçiş yaptım

( [ "$div" -gt "$num" ] ) && ...

... ve yukarıda 101'lik bir tavan için birkaç mermide yazdım ve dash.017 saniyede subshell olmadan ve 1.8 saniyede subshell ile yaptım. busybox.149 ve 2, zsh, 149 ve 4, bash.35 ve 6'da ve ksh93.149 ve .160'da. ksh93diğer mermilerde olduğu gibi deniz kabukları için çatal yapmaz. Belki de sorun, kabuk olduğu kadar alt kabuk değildir .


Avantajı nedir [ "$((...))" -eq "$((...))" ]üzerinde (( (...) == (...) ))? İkincisi daha az taşınabilir mi?
Ocak'ta 15:15

@ruakh - taşınabilirlik, hız, güvenilirlik. [ "$((...))" -eq "$((...)) ]kabuklarda çalışır yok programı çalıştırmak için 15 saniye sürebilir ve diğer değildir. Birinin diğerine üstünlüğü hiç kuşkusuz, o zaman bu sadece bir öncekinin üstünlüğünü sağlayabilir, bu da kullanmak için hiçbir zaman iyi bir neden olmadığı anlamına gelir (( (...) == (...) )).
mikeserv

Maalesef, cevabınız zaten kabuk desteği için ayrıntılı bir bilgiye sahip olduğumu varsayıyor gibi görünüyor (( ... )). Ben memnun oldum ama do o ayrıntılı bilgiye sahip değildir. (Unutma, (( ... ))daha az taşınabilir olup olmadığını sormuş olan benim .) Bu yüzden cevabını gerçekten anlamıyorum. : - / Biraz daha açık olabilir misiniz?
19

@ruakh - Üzgünüm ... Daha taşınabilir olup olmadığını sorduğunu, tam olarak nasıl avantajlı olduğunu sorduğunu görmedim - bu yüzden taşınabilirlik hakkında cevap verdim. Yine de, "$((...))"bir POSIX belirtilen ve diğer bir kabuk oluşumudur. POSIX mermileri oldukça yetenekli. Şube testlerini bile dashve poshdoğru bir şekilde halledersiniz "$((if_true ? (var=10) : (var=5) ))"ve daima $vardoğru şekilde atayın . busyboxorada kırılır - $if_truedeğeri ne olursa olsun her zaman iki tarafa değişir .
mikeserv

@ruakh - oh adamım. Bugün biraz kapalı olmalıyım ... tam şurada yazıyor ... ikincisi daha az taşınabilir mi? Bunu daha önce görmedim, sanırım ...?
mikeserv
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.