Ulangi String - Javascript

271

Apa metode terbaik atau paling ringkas untuk mengembalikan string yang diulang dalam jumlah yang sewenang-wenang?

Berikut ini adalah bidikan terbaik saya sejauh ini:

function repeat(s, n){
    var a = [];
    while(a.length < n){
        a.push(s);
    }
    return a.join('');
}
brad
sumber
5
Lebih dari 10 tahun yang lalu ada solusi terkenal saya untuk masalah ini, dan yang saya gunakan sebagai contoh dalam artikel optimasi JavaScript beberapa bulan sebelum Anda mengajukan pertanyaan ini: webreference.com/programming/javascript/jkm3/3 .html Rupanya, kebanyakan orang lupa tentang kode itu, dan saya tidak melihat solusi sebagus di bawah ini sebaik milik saya. Algoritma terbaik sepertinya diambil dari kode saya; kecuali karena kesalahpahaman tentang cara kode saya bekerja, ia melakukan satu langkah tambahan dari rangkaian eksponensial yang dihilangkan dalam dokumen asli saya dengan loop khusus.
Joseph Myers
10
Tidak ada yang mengangkat solusi Joseph. Algoritma ini berusia 3700 tahun. Biaya langkah tambahan dapat diabaikan. Dan artikel ini berisi kesalahan dan kesalahpahaman tentang penggabungan string dalam Javascript. Untuk siapa pun yang tertarik dengan bagaimana Javascript benar-benar menangani string secara internal, lihat Rope .
artistoex
4
Noone tampaknya telah memperhatikan bahwa pengulangan String protoype didefinisikan dan diimplementasikan, setidaknya di firefox.
kennebec
3
@kennebec: Ya, itu fitur EcmaScript 6 yang tidak ada ketika pertanyaan ini diajukan. Ini cukup didukung sekarang.
rvighne
3
@rvighne - Saya baru saja memeriksa kangax.github.io/compat-table/es6/#String.prototype.repeat Saya tidak akan menganggap dukungan secara eksklusif dari firefox dan chrome sebagai "cukup didukung"
aaaaaa

Jawaban:

406

Catatan untuk pembaca baru: Jawaban ini sudah lama dan tidak terlalu praktis - ini hanya "pintar" karena menggunakan hal-hal Array untuk menyelesaikan String. Ketika saya menulis "lebih sedikit proses" saya pasti berarti "lebih sedikit kode" karena, seperti yang orang lain catat dalam jawaban berikutnya, ia berfungsi seperti babi. Jadi jangan menggunakannya jika kecepatan penting bagi Anda.

Saya akan menempatkan fungsi ini ke objek String secara langsung. Alih-alih membuat array, mengisinya, dan bergabung dengan char kosong, buat saja array dengan panjang yang sesuai, dan bergabunglah dengan string yang Anda inginkan. Hasil yang sama, lebih sedikit proses!

String.prototype.repeat = function( num )
{
    return new Array( num + 1 ).join( this );
}

alert( "string to repeat\n".repeat( 4 ) );
Peter Bailey
sumber
36
Saya mencoba untuk tidak memperluas benda-benda asli, tetapi selain itu ini adalah solusi yang indah. Terima kasih!
brad
34
@ brad - mengapa tidak? Anda lebih suka mencemari namespace global dengan fungsi yang memiliki rumah yang didefinisikan dengan cukup baik (objek String)?
Peter Bailey
16
Sebenarnya, kedua argumen Anda juga berlaku untuk namespace global. Jika saya akan memperluas namespace dan memiliki potensi tabrakan, saya lebih suka melakukannya 1) tidak di global 2) di salah satu yang relevan dan 3) mudah untuk refactor. Ini berarti meletakkannya di prototipe String, bukan di global.
Peter Bailey
11
satu perubahan yang saya buat untuk fungsi ini adalah menempatkan parseInt () di sekitar "num", karena jika Anda memiliki string numerik, Anda mungkin mendapatkan perilaku aneh karena jenis juggling JS. misalnya: "my string" .repeat ("6") == "61"
nickf
19
Jika Anda tidak ingin memperpanjang benda asli, Anda bisa menempatkan fungsi pada objek String sebaliknya, seperti ini: String.repeat = function(string, num){ return new Array(parseInt(num) + 1).join(string); };. Sebut saja seperti ini:String.repeat('/\', 20)
Znarkus
204

Saya telah menguji kinerja semua pendekatan yang diusulkan.

Inilah varian tercepat yang saya miliki.

String.prototype.repeat = function(count) {
    if (count < 1) return '';
    var result = '', pattern = this.valueOf();
    while (count > 1) {
        if (count & 1) result += pattern;
        count >>= 1, pattern += pattern;
    }
    return result + pattern;
};

Atau sebagai fungsi yang berdiri sendiri :

function repeat(pattern, count) {
    if (count < 1) return '';
    var result = '';
    while (count > 1) {
        if (count & 1) result += pattern;
        count >>= 1, pattern += pattern;
    }
    return result + pattern;
}

Ini didasarkan pada algoritma artistoex . Ini sangat cepat. Dan semakin besar count, semakin cepat dibandingkan dengan new Array(count + 1).join(string)pendekatan tradisional .

Saya hanya mengubah 2 hal:

  1. diganti pattern = thisdengan pattern = this.valueOf()(menghapus satu konversi jenis yang jelas);
  2. menambahkan if (count < 1)pemeriksaan dari prototypejs ke bagian atas fungsi untuk mengecualikan tindakan yang tidak perlu dalam kasus itu.
  3. pengoptimalan terapan dari jawaban Dennis (kecepatan 5-7%)

UPD

Menciptakan taman bermain pengujian kecil di sini untuk mereka yang tertarik.

variabel count~ 0 .. 100:

Diagram kinerja

konstan count= 1024:

Diagram kinerja

Gunakan dan buat lebih cepat jika bisa :)

dikecewakan
sumber
4
Kerja bagus! Saya pikir count < 1kasus ini optimasi yang benar-benar tidak perlu.
JayVee
Algoritma yang sangat baik O (log N). Terima kasih atas pengoptimalan hebat dengan valueOf ()
vp_arth
2
Tautan gambar sudah mati.
Benjamin Gruenbaum
Tautan baik-baik saja. Mungkin tidak tersedia untuk sementara waktu
rusak
Tes JSFiddle tidak berfungsi dengan benar lagi; tampaknya terus menjalankan fungsi pertama berulang kali (membiarkannya berjalan setengah jam untuk memastikan)
RevanProdigalKnight
47

Masalah ini adalah masalah optimisasi JavaScript yang terkenal / "klasik", disebabkan oleh fakta bahwa string JavaScript "tidak dapat diubah" dan penambahan dengan penggabungan bahkan satu karakter ke string memerlukan kreasi, termasuk alokasi memori untuk dan penyalinan ke , seluruh string baru.

Sayangnya, jawaban yang diterima pada halaman ini salah, di mana "salah" berarti oleh faktor kinerja 3x untuk string satu karakter sederhana, dan 8x-97x untuk string pendek yang diulang lebih sering, hingga 300x untuk kalimat berulang, dan jauh salah ketika mengambil batas rasio kompleksitas algoritma nhingga tak terhingga. Juga, ada jawaban lain di halaman ini yang hampir benar (berdasarkan salah satu dari banyak generasi dan variasi solusi yang tepat yang beredar di seluruh Internet dalam 13 tahun terakhir). Namun, solusi "hampir tepat" ini melewatkan titik kunci dari algoritma yang benar yang menyebabkan penurunan kinerja 50%.

Hasil Kinerja JS untuk jawaban yang diterima, jawaban lain dengan performa terbaik (berdasarkan versi terdegradasi dari algoritma asli dalam jawaban ini), dan jawaban ini menggunakan algoritme saya yang dibuat 13 tahun yang lalu

~ Oktober 2000 Saya menerbitkan algoritma untuk masalah yang tepat ini yang secara luas diadaptasi, dimodifikasi, kemudian akhirnya kurang dipahami dan dilupakan. Untuk mengatasi masalah ini, pada bulan Agustus 2008 saya menerbitkan sebuah artikel http://www.webreference.com/programming/javascript/jkm3/3.html menjelaskan algoritma dan menggunakannya sebagai contoh sederhana pengoptimalan tujuan umum JavaScript. Sekarang, Referensi Web telah menggosok informasi kontak saya dan bahkan nama saya dari artikel ini. Dan sekali lagi, algoritme telah banyak diadaptasi, dimodifikasi, kemudian kurang dipahami dan sebagian besar dilupakan.

Algoritme pengulangan / perkalian string asli oleh Joseph Myers, sekitar Y2K sebagai fungsi pengganda teks dalam Text.js; diterbitkan Agustus 2008 dalam bentuk ini dengan Referensi Web: http://www.webreference.com/programming/javascript/jkm3/3.html (Artikel ini menggunakan fungsi sebagai contoh optimasi JavaScript, yang merupakan satu-satunya untuk yang aneh nama "stringFill3.")

/*
 * Usage: stringFill3("abc", 2) == "abcabc"
 */

function stringFill3(x, n) {
    var s = '';
    for (;;) {
        if (n & 1) s += x;
        n >>= 1;
        if (n) x += x;
        else break;
    }
    return s;
}

Dalam waktu dua bulan setelah publikasi artikel itu, pertanyaan yang sama ini diposting ke Stack Overflow dan terbang di bawah radar saya sampai sekarang, ketika tampaknya algoritma asli untuk masalah ini sekali lagi dilupakan. Solusi terbaik yang tersedia di halaman Stack Overflow ini adalah versi modifikasi dari solusi saya, mungkin dipisahkan oleh beberapa generasi. Sayangnya, modifikasi merusak optimalitas solusi. Bahkan, dengan mengubah struktur loop dari yang asli, solusi yang dimodifikasi melakukan langkah ekstra yang sama sekali tidak diperlukan dari duplikasi eksponensial (sehingga bergabung dengan string terbesar yang digunakan dalam jawaban yang tepat dengan dirinya sendiri waktu tambahan dan kemudian membuangnya).

Di bawah ini terjadi diskusi tentang beberapa optimasi JavaScript yang terkait dengan semua jawaban untuk masalah ini dan untuk kepentingan semua.

Teknik: Hindari referensi ke objek atau properti objek

Untuk menggambarkan bagaimana teknik ini bekerja, kami menggunakan fungsi JavaScript kehidupan nyata yang menciptakan string dengan panjang berapa pun yang dibutuhkan. Dan seperti yang akan kita lihat, lebih banyak optimasi dapat ditambahkan!

Fungsi seperti yang digunakan di sini adalah membuat padding untuk meluruskan kolom teks, untuk memformat uang, atau untuk mengisi data blok hingga batas. Fungsi pembuatan teks juga memungkinkan input panjang variabel untuk menguji fungsi lain yang beroperasi pada teks. Fungsi ini adalah salah satu komponen penting dari modul pemrosesan teks JavaScript.

Saat kami melanjutkan, kami akan membahas dua teknik optimasi yang paling penting sambil mengembangkan kode asli menjadi algoritma yang dioptimalkan untuk membuat string. Hasil akhirnya adalah kekuatan industri, fungsi kinerja tinggi yang saya gunakan di mana-mana - menyelaraskan harga barang dan total dalam formulir pemesanan JavaScript, pemformatan data, dan pemformatan pesan email / teks dan banyak kegunaan lainnya.

Kode asli untuk membuat string stringFill1()

function stringFill1(x, n) { 
    var s = ''; 
    while (s.length < n) s += x; 
    return s; 
} 
/* Example of output: stringFill1('x', 3) == 'xxx' */ 

Sintaksnya di sini jelas. Seperti yang Anda lihat, kami telah menggunakan variabel fungsi lokal, sebelum melanjutkan ke optimasi lainnya.

Perlu diketahui bahwa ada satu referensi tidak bersalah ke properti objek s.lengthdalam kode yang merusak kinerjanya. Lebih buruk lagi, penggunaan properti objek ini mengurangi kesederhanaan program dengan membuat asumsi bahwa pembaca tahu tentang properti objek string JavaScript.

Penggunaan properti objek ini merusak keumuman program komputer. Program mengasumsikan bahwa xharus berupa string yang panjang. Ini membatasi penerapan stringFill1()fungsi hanya untuk pengulangan karakter tunggal. Bahkan karakter tunggal tidak dapat digunakan jika mengandung banyak byte seperti entitas HTML &nbsp;.

Masalah terburuk yang disebabkan oleh penggunaan properti objek yang tidak perlu ini adalah bahwa fungsi tersebut menciptakan loop tak terbatas jika diuji pada string input kosong x. Untuk memeriksa sifat umum, terapkan suatu program dengan jumlah input sekecil mungkin. Suatu program yang mogok ketika diminta untuk melebihi jumlah memori yang tersedia memiliki alasan. Program seperti ini yang macet saat diminta tidak menghasilkan apa-apa tidak dapat diterima. Terkadang kode cantik adalah kode beracun.

Kesederhanaan mungkin merupakan tujuan ganda dari pemrograman komputer, tetapi umumnya tidak. Ketika suatu program tidak memiliki tingkat umum yang masuk akal, itu tidak sah untuk mengatakan, "Program ini cukup baik sejauh itu berjalan." Seperti yang Anda lihat, menggunakan string.lengthproperti mencegah program ini bekerja dalam pengaturan umum, dan pada kenyataannya, program yang salah siap menyebabkan peramban atau kerusakan sistem.

Apakah ada cara untuk meningkatkan kinerja JavaScript ini serta merawat dua masalah serius ini?

Tentu saja. Cukup gunakan bilangan bulat.

Kode yang dioptimalkan untuk membuat string stringFill2()

function stringFill2(x, n) { 
    var s = ''; 
    while (n-- > 0) s += x; 
    return s; 
} 

Kode waktu untuk membandingkan stringFill1()danstringFill2()

function testFill(functionToBeTested, outputSize) { 
    var i = 0, t0 = new Date(); 
    do { 
        functionToBeTested('x', outputSize); 
        t = new Date() - t0; 
        i++; 
    } while (t < 2000); 
    return t/i/1000; 
} 
seconds1 = testFill(stringFill1, 100); 
seconds2 = testFill(stringFill2, 100); 

Keberhasilan sejauh ini stringFill2()

stringFill1()membutuhkan 47.297 mikrodetik (sepersejuta detik) untuk mengisi string 100-byte, dan stringFill2()membutuhkan 27,68 mikrodetik untuk melakukan hal yang sama. Itu hampir dua kali lipat dalam kinerja dengan menghindari referensi ke properti objek.

Teknik: Hindari menambahkan string pendek ke string panjang

Hasil kami sebelumnya terlihat bagus - sangat bagus, sebenarnya. Fungsi yang ditingkatkan stringFill2()jauh lebih cepat karena penggunaan dua optimasi pertama kami. Percayakah Anda jika saya memberi tahu Anda bahwa itu dapat ditingkatkan menjadi jauh lebih cepat daripada sekarang?

Ya, kita dapat mencapai tujuan itu. Saat ini kita perlu menjelaskan bagaimana kita menghindari menambahkan string pendek ke string panjang.

Perilaku jangka pendek tampaknya cukup baik, dibandingkan dengan fungsi asli kami. Ilmuwan komputer suka menganalisis "perilaku asimptotik" dari suatu fungsi atau algoritma program komputer, yang berarti mempelajari perilaku jangka panjangnya dengan mengujinya dengan input yang lebih besar. Terkadang tanpa melakukan tes lebih lanjut, seseorang tidak pernah menyadari cara-cara program komputer dapat ditingkatkan. Untuk melihat apa yang akan terjadi, kita akan membuat string 200-byte.

Masalah yang muncul dengan stringFill2()

Menggunakan fungsi timing kami, kami menemukan bahwa waktu meningkat menjadi 62,54 mikrodetik untuk string 200-byte, dibandingkan dengan 27,68 untuk string 100-byte. Sepertinya waktunya harus dua kali lipat untuk melakukan pekerjaan dua kali lebih banyak, tetapi justru berlipat tiga atau empat kali lipat. Dari pengalaman pemrograman, hasil ini tampak aneh, karena jika ada, fungsi harus sedikit lebih cepat karena pekerjaan dilakukan lebih efisien (200 byte per panggilan fungsi daripada 100 byte per panggilan fungsi). Masalah ini berkaitan dengan properti berbahaya dari string JavaScript: String JavaScript "tidak dapat diubah."

Abadi berarti Anda tidak dapat mengubah string setelah itu dibuat. Dengan menambahkan satu byte pada satu waktu, kami tidak menggunakan upaya satu byte lagi. Kami benar-benar menciptakan kembali seluruh string ditambah satu byte lagi.

Efeknya, untuk menambahkan satu byte lagi ke string 100-byte, dibutuhkan kerja senilai 101 byte. Mari kita secara singkat menganalisis biaya komputasi untuk membuat serangkaian Nbyte. Biaya penambahan byte pertama adalah 1 unit upaya komputasi. Biaya menambahkan byte kedua bukan satu unit tetapi 2 unit (menyalin byte pertama ke objek string baru serta menambahkan byte kedua). Bita ketiga membutuhkan biaya 3 unit, dll.

C(N) = 1 + 2 + 3 + ... + N = N(N+1)/2 = O(N^2). Simbol O(N^2)diucapkan Big O dari N kuadrat, dan itu berarti bahwa biaya komputasi dalam jangka panjang sebanding dengan kuadrat dari panjang string. Untuk membuat 100 karakter dibutuhkan 10.000 unit kerja, dan untuk membuat 200 karakter dibutuhkan 40.000 unit kerja.

Inilah sebabnya mengapa dibutuhkan lebih dari dua kali lebih lama untuk membuat 200 karakter dari 100 karakter. Padahal, seharusnya memakan waktu empat kali lebih lama. Pengalaman pemrograman kami benar karena pekerjaan dilakukan sedikit lebih efisien untuk string yang lebih lama, dan karenanya hanya butuh sekitar tiga kali lebih lama. Setelah overhead panggilan fungsi diabaikan untuk berapa lama string yang kita buat, sebenarnya akan membutuhkan waktu empat kali lebih banyak untuk membuat string dua kali lebih lama.

(Catatan historis: Analisis ini tidak selalu berlaku untuk string dalam kode sumber, seperti html = 'abcd\n' + 'efgh\n' + ... + 'xyz.\n', karena kompiler kode sumber JavaScript dapat menggabungkan string sebelum menjadikannya menjadi objek string JavaScript. Hanya beberapa tahun yang lalu, implementasi KJS dari JavaScript akan membeku atau mogok saat memuat string panjang kode sumber yang bergabung dengan tanda plus. Karena waktu komputasi O(N^2)itu tidak sulit untuk membuat halaman Web yang membebani browser Web Konqueror atau Safari, yang menggunakan inti mesin KJS JavaScript. Saya pertama kali menemukan masalah ini ketika saya sedang mengembangkan bahasa markup dan parser bahasa markup JavaScript, dan kemudian saya menemukan apa yang menyebabkan masalah ketika saya menulis skrip saya untuk Termasuk JavaScript.)

Jelas penurunan kinerja yang cepat ini adalah masalah besar. Bagaimana kita bisa menghadapinya, mengingat kita tidak bisa mengubah cara JavaScript menangani string sebagai objek yang tidak dapat diubah? Solusinya adalah dengan menggunakan algoritma yang menciptakan string sesering mungkin.

Untuk memperjelas, tujuan kami adalah untuk menghindari menambahkan string pendek ke string panjang, karena untuk menambahkan string pendek, seluruh string panjang juga harus diduplikasi.

Bagaimana algoritma bekerja untuk menghindari penambahan string pendek ke string panjang

Berikut adalah cara yang baik untuk mengurangi berapa kali objek string baru dibuat. Menggabungkan panjang string yang lebih panjang sehingga lebih dari satu byte pada suatu waktu ditambahkan ke output.

Misalnya, untuk membuat string panjang N = 9:

x = 'x'; 
s = ''; 
s += x; /* Now s = 'x' */ 
x += x; /* Now x = 'xx' */ 
x += x; /* Now x = 'xxxx' */ 
x += x; /* Now x = 'xxxxxxxx' */ 
s += x; /* Now s = 'xxxxxxxxx' as desired */

Melakukan ini diperlukan membuat string dengan panjang 1, membuat string dengan panjang 2, membuat string dengan panjang 4, membuat string dengan panjang 8, dan akhirnya, membuat string dengan panjang 9. Berapa biaya yang telah kita hemat?

Biaya lama C(9) = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 9 = 45.

Biaya baru C(9) = 1 + 2 + 4 + 8 + 9 = 24.

Perhatikan bahwa kita harus menambahkan string dengan panjang 1 ke string dengan panjang 0, kemudian string dengan panjang 1 ke string dengan panjang 1, kemudian string dengan panjang 2 ke string dengan panjang 2, kemudian string dengan panjang 4 ke string dengan panjang 4, lalu string dengan panjang 8 ke string dengan panjang 1, untuk mendapatkan string dengan panjang 9. Apa yang kita lakukan dapat diringkas sebagai menghindari menambahkan string pendek ke string panjang, atau di lain kata-kata, mencoba menyatukan string yang panjangnya sama atau hampir sama.

Untuk biaya komputasi lama kami menemukan rumus N(N+1)/2. Apakah ada formula untuk biaya baru? Ya, tapi rumit. Yang penting adalah itu O(N), dan menggandakan panjang tali akan kira-kira dua kali lipat jumlah pekerjaan daripada empat kali lipatnya.

Kode yang mengimplementasikan ide baru ini hampir sama rumitnya dengan rumus untuk biaya komputasi. Ketika Anda membacanya, ingat itu >>= 1artinya bergeser ke kanan sebesar 1 byte. Jadi jika n = 10011angka biner, maka n >>= 1menghasilkan nilai n = 1001.

Bagian lain dari kode yang mungkin tidak Anda kenali adalah bitwise dan operator, ditulis &. Ekspresi n & 1bernilai true jika digit biner terakhir nadalah 1, dan false jika digit biner terakhir nadalah 0.

stringFill3()Fungsi baru yang sangat efisien

function stringFill3(x, n) { 
    var s = ''; 
    for (;;) { 
        if (n & 1) s += x; 
        n >>= 1; 
        if (n) x += x; 
        else break; 
    } 
    return s; 
} 

Itu terlihat jelek bagi mata yang tidak terlatih, tetapi kinerjanya tidak kalah indah.

Mari kita lihat seberapa baik fungsi ini bekerja. Setelah melihat hasilnya, kemungkinan Anda tidak akan pernah melupakan perbedaan antara suatu O(N^2)algoritma dan O(N)algoritma.

stringFill1()membutuhkan 88,7 mikrodetik (sepersejuta detik) untuk membuat string 200-byte, stringFill2()membutuhkan 62,54, dan stringFill3()hanya membutuhkan 4,608. Apa yang membuat algoritma ini jauh lebih baik? Semua fungsi mengambil keuntungan dari menggunakan variabel fungsi lokal, tetapi mengambil keuntungan dari teknik optimasi kedua dan ketiga menambahkan peningkatan dua kali lipat untuk kinerja stringFill3().

Analisis yang lebih dalam

Apa yang membuat fungsi khusus ini membuat kompetisi keluar dari air?

Seperti yang saya sebutkan, alasan kedua fungsi ini, stringFill1()dan stringFill2(), berjalan sangat lambat adalah bahwa string JavaScript tidak dapat diubah. Memori tidak dapat dialokasikan kembali untuk memungkinkan satu byte lagi pada satu waktu ditambahkan ke data string yang disimpan oleh JavaScript. Setiap kali satu byte lagi ditambahkan ke ujung string, seluruh string dibuat ulang dari awal hingga akhir.

Dengan demikian, untuk meningkatkan kinerja skrip, kita harus melakukan pra-string string yang lebih panjang dengan menggabungkan dua string bersama sebelumnya, dan kemudian secara rekursif membangun panjang string yang diinginkan.

Misalnya, untuk membuat string byte 16 huruf, pertama string dua byte akan dikomputasi. Kemudian string dua byte akan digunakan kembali untuk precompute string empat byte. Kemudian string empat byte akan digunakan kembali untuk precompute string delapan byte. Akhirnya, dua string delapan byte akan digunakan kembali untuk membuat string baru yang diinginkan sebesar 16 byte. Secara keseluruhan empat string baru harus dibuat, satu panjang 2, satu panjang 4, satu panjang 8 dan satu panjang 16. Total biaya adalah 2 + 4 + 8 + 16 = 30.

Dalam jangka panjang efisiensi ini dapat dihitung dengan menambahkan urutan terbalik dan menggunakan deret geometri yang dimulai dengan suku pertama a1 = N dan memiliki rasio umum r = 1/2. Jumlah deret geometri diberikan oleh a_1 / (1-r) = 2N.

Ini lebih efisien daripada menambahkan satu karakter untuk membuat string baru dengan panjang 2, membuat string baru dengan panjang 3, 4, 5, dan seterusnya, sampai 16. Algoritma sebelumnya menggunakan proses penambahan byte tunggal pada suatu waktu , dan total biaya itu akan menjadi n (n + 1) / 2 = 16 (17) / 2 = 8 (17) = 136.

Jelas, 136 adalah angka yang jauh lebih besar dari 30, dan algoritma sebelumnya membutuhkan lebih banyak waktu untuk membangun string.

Untuk membandingkan dua metode, Anda dapat melihat seberapa cepat algoritma rekursif (juga disebut "divide and conquer") ada pada string dengan panjang 123.457. Pada komputer FreeBSD saya, algoritma ini, diimplementasikan dalam stringFill3()fungsi, membuat string dalam 0,001058 detik, sedangkan stringFill1()fungsi asli membuat string dalam 0,0808 detik. Fungsi baru 76 kali lebih cepat.

Perbedaan kinerja tumbuh seiring panjangnya string menjadi lebih besar. Dalam batas sebagai string yang lebih besar dan lebih besar dibuat, fungsi asli berperilaku kira-kira seperti waktu C1(konstan) N^2, dan fungsi baru berperilaku seperti waktu C2(konstan) N.

Dari percobaan kami, kami dapat menentukan nilai C1menjadi C1 = 0.0808 / (123457)2 = .00000000000530126997, dan nilai C2menjadi C2 = 0.001058 / 123457 = .00000000856978543136. Dalam 10 detik, fungsi baru dapat membuat string berisi 1.166.890.359 karakter. Untuk membuat string yang sama ini, fungsi lama akan membutuhkan waktu 7.218.384 detik.

Ini hampir tiga bulan dibandingkan dengan sepuluh detik!

Saya hanya menjawab (beberapa tahun terlambat) karena solusi asli saya untuk masalah ini telah beredar di Internet selama lebih dari 10 tahun, dan tampaknya masih kurang dipahami oleh beberapa orang yang mengingatnya. Saya berpikir bahwa dengan menulis artikel tentang itu di sini saya akan membantu:

Optimalisasi Kinerja untuk JavaScript Kecepatan Tinggi / Halaman 3

Sayangnya, beberapa solusi lain yang disajikan di sini masih beberapa yang membutuhkan waktu tiga bulan untuk menghasilkan jumlah output yang sama dengan yang dihasilkan oleh solusi yang tepat dalam 10 detik.

Saya ingin meluangkan waktu untuk mereproduksi bagian dari artikel di sini sebagai jawaban kanonik pada Stack Overflow.

Perhatikan bahwa algoritma berkinerja terbaik di sini jelas berdasarkan pada algoritme saya dan mungkin diturunkan dari adaptasi generasi ke-3 atau ke-4 orang lain. Sayangnya, modifikasi mengakibatkan pengurangan kinerjanya. Variasi solusi saya yang disajikan di sini mungkin tidak mengerti for (;;)ekspresi membingungkan saya yang terlihat seperti loop tak terbatas utama dari server yang ditulis dalam C, dan yang hanya dirancang untuk memungkinkan pernyataan break diposisikan dengan hati-hati untuk kontrol loop, cara paling ringkas untuk hindari mereplikasi string secara eksponensial satu waktu ekstra yang tidak perlu.

Joseph Myers
sumber
4
Jawaban ini seharusnya tidak menerima banyak upvotes. Pertama-tama, klaim kepemilikan oleh Joseph adalah ridiculuou. Algoritma yang mendasari adalah 3700 tahun.
artistoex
2
Kedua, mengandung banyak informasi yang salah. Implementasi Javascript modern bahkan tidak menyentuh isi string ketika melakukan concatenation (v8 mewakili string concatenated sebagai objek bertipe ConsString). Semua perangkat tambahan yang tersisa dapat diabaikan (dalam hal kompleksitas asimptotik).
artistoex
3
Gagasan Anda tentang bagaimana string digabungkan adalah salah. Untuk menggabungkan dua string, Javascript sama sekali tidak membaca byte dari string konstituen. Sebaliknya, itu hanya menciptakan objek yang mengacu pada bagian kiri dan kanan. Inilah sebabnya mengapa rangkaian terakhir dalam loop tidak lebih mahal daripada yang pertama.
artistoex
3
Tentu saja, ini menghasilkan biaya yang lebih besar daripada O (1) untuk mengindeks string, sehingga rangkai dapat diratakan nanti yang memang perlu evaluasi lebih lanjut.
artistoex
1
Ini adalah bacaan yang sangat baik. Anda harus menulis buku tentang efisiensi dan semua itu!
39

Yang ini cukup efisien

String.prototype.repeat = function(times){
    var result="";
    var pattern=this;
    while (times > 0) {
        if (times&1)
            result+=pattern;
        times>>=1;
        pattern+=pattern;
    }
    return result;
};
artisoex
sumber
11
@Olegs, saya pikir ide untuk memilih kurang dari memilih untuk seseorang atau untuk kreativitas seseorang (yang memang patut dipuji), tetapi idenya adalah memilih solusi yang paling lengkap, sehingga dapat dengan mudah ditemukan di teratas dalam daftar, tanpa harus membaca semua jawaban dalam mencari yang sempurna. (Karena, sayangnya, kita semua memiliki waktu terbatas ...)
Sorin Postelnicu
38

Kabar baik! String.prototype.repeatadalah sekarang menjadi bagian dari JavaScript .

"yo".repeat(2);
// returns: "yoyo"

Metode ini didukung oleh semua browser utama, kecuali Internet Explorer dan Android Webview. Untuk daftar terbaru, lihat MDN: String.prototype.repeat> Kompatibilitas browser .

MDN memiliki polyfill untuk browser tanpa dukungan.

André Laszlo
sumber
Terima kasih melaporkan keadaan saat ini, meskipun saya pikir polyfill Mozilla adalah cara yang rumit untuk sebagian besar kebutuhan (saya berasumsi mereka mencoba meniru perilaku implementasi C efisien mereka) - jadi tidak akan benar-benar menjawab persyaratan OP untuk kesederhanaan. Salah satu pendekatan lain yang ditetapkan sebagai polyfill pasti akan lebih ringkas ;-)
Guss
2
Pastinya! Tetapi menggunakan built-in harus menjadi versi yang paling ringkas. Karena polyfill pada dasarnya hanya back-port, mereka cenderung sedikit rumit untuk memastikan kompatibilitas dengan spesifikasi (atau spesifikasi yang diusulkan, dalam hal ini). Saya menambahkannya untuk kelengkapan, terserah OP untuk memutuskan metode mana yang akan digunakan, saya kira.
André Laszlo
19

String.prototype.repeat sekarang Standar ES6.

'abc'.repeat(3); //abcabcabc
Lewis
sumber
bagus! .. tetapi tidak bisa digunakan untuk saya (itu tidak didukung pada ios <9): kangax.github.io/compat-table/es6
Benjamin
@Benjamin Gunakan polyfill pada tautan.
Lewis
Saya pikir harus menjadi jawaban yang disematkan .
test30
17

Memperluas solusi P. Bailey :

String.prototype.repeat = function(num) {
    return new Array(isNaN(num)? 1 : ++num).join(this);
    }

Dengan cara ini Anda harus aman dari tipe argumen yang tidak terduga:

var foo = 'bar';
alert(foo.repeat(3));              // Will work, "barbarbar"
alert(foo.repeat('3'));            // Same as above
alert(foo.repeat(true));           // Same as foo.repeat(1)

alert(foo.repeat(0));              // This and all the following return an empty
alert(foo.repeat(false));          // string while not causing an exception
alert(foo.repeat(null));
alert(foo.repeat(undefined));
alert(foo.repeat({}));             // Object
alert(foo.repeat(function () {})); // Function

EDIT: Kredit untuk melakukan jerone untuk ++numidenya yang elegan !

antikris
sumber
2
Mengubah sedikit Anda:String.prototype.repeat = function(n){return new Array(isNaN(n) ? 1 : ++n).join(this);}
Jerone
Bagaimanapun menurut tes ini ( jsperf.com/string-repeat/2 ) melakukan loop sederhana dengan string concatanation nampaknya jauh lebih cepat di Chrome dibandingkan menggunakan Array.join. Lucu kan ?!
Marco Demaio
8

Menggunakan Array(N+1).join("string_to_repeat")

Kalpesh Patel
sumber
Saya suka ini. Idk mengapa tidak ada di sana.
Joe Thomas
suka. begitu sederhana dan bersih
ekkis
5
/**  
@desc: repeat string  
@param: n - times  
@param: d - delimiter  
*/

String.prototype.repeat = function (n, d) {
    return --n ? this + (d || '') + this.repeat(n, d) : '' + this
};

ini adalah cara mengulangi string beberapa kali menggunakan delimeter.

BitOfUniverse
sumber
4

Berikut ini adalah peningkatan 5-7% pada jawaban disfated

Buka gulungannya dengan berhenti count > 1dan lakukan result += pattnernkonser tambahan setelah putaran itu. Ini akan menghindari putaran final yang sebelumnya tidak digunakan pattern += patterntanpa harus menggunakan if-check mahal. Hasil akhirnya akan terlihat seperti ini:

String.prototype.repeat = function(count) {
    if (count < 1) return '';
    var result = '', pattern = this.valueOf();
    while (count > 1) {
        if (count & 1) result += pattern;
        count >>= 1, pattern += pattern;
    }
    result += pattern;
    return result;
};

Dan ini biola disfated yang bercabang untuk versi yang belum dibuka: http://jsfiddle.net/wsdfg/

Dennis
sumber
2
function repeat(s, n) { var r=""; for (var a=0;a<n;a++) r+=s; return r;}
Joel Coehoorn
sumber
2
Bukankah penggabungan string mahal? Setidaknya itu yang terjadi di Jawa.
Vijay Dev
Kenapa ya mereka. Namun, itu tidak bisa benar-benar dioptimalkan dalam javarscript. :(
McTrafik
Bagaimana dengan peningkatan kinerja ini: var r=s; for (var a=1;...:)))) Bagaimanapun menurut tes ini ( jsperf.com/string-repeat/2 ) melakukan loop sederhana dengan rangkaian string seperti apa yang Anda sarankan tampaknya jauh lebih cepat di Chrome dibandingkan menggunakan Array .Ikuti.
Marco Demaio
@VijayDev - tidak sesuai dengan tes ini: jsperf.com/ultimate-concat-vs-join
jbyrd
2

Pengujian berbagai metode:

var repeatMethods = {
    control: function (n,s) {
        /* all of these lines are common to all methods */
        if (n==0) return '';
        if (n==1 || isNaN(n)) return s;
        return '';
    },
    divideAndConquer:   function (n, s) {
        if (n==0) return '';
        if (n==1 || isNaN(n)) return s;
        with(Math) { return arguments.callee(floor(n/2), s)+arguments.callee(ceil(n/2), s); }
    },
    linearRecurse: function (n,s) {
        if (n==0) return '';
        if (n==1 || isNaN(n)) return s;
        return s+arguments.callee(--n, s);
    },
    newArray: function (n, s) {
        if (n==0) return '';
        if (n==1 || isNaN(n)) return s;
        return (new Array(isNaN(n) ? 1 : ++n)).join(s);
    },
    fillAndJoin: function (n, s) {
        if (n==0) return '';
        if (n==1 || isNaN(n)) return s;
        var ret = [];
        for (var i=0; i<n; i++)
            ret.push(s);
        return ret.join('');
    },
    concat: function (n,s) {
        if (n==0) return '';
        if (n==1 || isNaN(n)) return s;
        var ret = '';
        for (var i=0; i<n; i++)
            ret+=s;
        return ret;
    },
    artistoex: function (n,s) {
        var result = '';
        while (n>0) {
            if (n&1) result+=s;
            n>>=1, s+=s;
        };
        return result;
    }
};
function testNum(len, dev) {
    with(Math) { return round(len+1+dev*(random()-0.5)); }
}
function testString(len, dev) {
    return (new Array(testNum(len, dev))).join(' ');
}
var testTime = 1000,
    tests = {
        biggie: { str: { len: 25, dev: 12 }, rep: {len: 200, dev: 50 } },
        smalls: { str: { len: 5, dev: 5}, rep: { len: 5, dev: 5 } }
    };
var testCount = 0;
var winnar = null;
var inflight = 0;
for (var methodName in repeatMethods) {
    var method = repeatMethods[methodName];
    for (var testName in tests) {
        testCount++;
        var test = tests[testName];
        var testId = methodName+':'+testName;
        var result = {
            id: testId,
            testParams: test
        }
        result.count=0;

        (function (result) {
            inflight++;
            setTimeout(function () {
                result.start = +new Date();
                while ((new Date() - result.start) < testTime) {
                    method(testNum(test.rep.len, test.rep.dev), testString(test.str.len, test.str.dev));
                    result.count++;
                }
                result.end = +new Date();
                result.rate = 1000*result.count/(result.end-result.start)
                console.log(result);
                if (winnar === null || winnar.rate < result.rate) winnar = result;
                inflight--;
                if (inflight==0) {
                    console.log('The winner: ');
                    console.log(winnar);
                }
            }, (100+testTime)*testCount);
        }(result));
    }
}
Fordi
sumber
2

Ini versi aman JSLint

String.prototype.repeat = function (num) {
  var a = [];
  a.length = num << 0 + 1;
  return a.join(this);
};
Erik Aigner
sumber
2

Untuk semua browser

Ini tentang singkat seperti yang didapat:

function repeat(s, n) { return new Array(n+1).join(s); }

Jika Anda juga peduli dengan kinerja, ini adalah pendekatan yang jauh lebih baik:

function repeat(s, n) { var a=[],i=0;for(;i<n;)a[i++]=s;return a.join(''); }

Jika Anda ingin membandingkan kinerja kedua opsi, lihat Fiddle dan Fiddle ini untuk tes benchmark. Selama pengujian saya sendiri, opsi kedua adalah sekitar 2 kali lebih cepat di Firefox dan sekitar 4 kali lebih cepat di Chrome!

Hanya untuk peramban modern:

Di browser modern, kini Anda juga dapat melakukan ini:

function repeat(s,n) { return s.repeat(n) };

Opsi ini tidak hanya lebih pendek dari kedua opsi lainnya, tetapi bahkan lebih cepat dari opsi kedua.

Sayangnya, itu tidak berfungsi di versi Internet Explorer apa pun. Angka-angka dalam tabel menentukan versi browser pertama yang sepenuhnya mendukung metode ini:

masukkan deskripsi gambar di sini

John Slegers
sumber
2

Hanya fungsi pengulangan lainnya:

function repeat(s, n) {
  var str = '';
  for (var i = 0; i < n; i++) {
    str += s;
  }
  return str;
}
oboshto
sumber
2

ES2015telah menyadari repeat()metode ini !

http://www.ecma-international.org/ecma-262/6.0/#sec-string.prototype.repeat
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ String / ulangi
http://www.w3schools.com/jsref/jsref_repeat.asp

/** 
 * str: String
 * count: Number
 */
const str = `hello repeat!\n`, count = 3;

let resultString = str.repeat(count);

console.log(`resultString = \n${resultString}`);
/*
resultString = 
hello repeat!
hello repeat!
hello repeat!
*/

({ toString: () => 'abc', repeat: String.prototype.repeat }).repeat(2);
// 'abcabc' (repeat() is a generic method)

// Examples

'abc'.repeat(0);    // ''
'abc'.repeat(1);    // 'abc'
'abc'.repeat(2);    // 'abcabc'
'abc'.repeat(3.5);  // 'abcabcabc' (count will be converted to integer)
// 'abc'.repeat(1/0);  // RangeError
// 'abc'.repeat(-1);   // RangeError

xgqfrms
sumber
1

Ini mungkin yang rekursif terkecil: -

String.prototype.repeat = function(n,s) {
s = s || ""
if(n>0) {
   s += this
   s = this.repeat(--n,s)
}
return s}
John
sumber
1

Rangkaian rekursif sederhana

Saya hanya ingin mencobanya, dan membuat ini:

function ditto( s, r, c ) {
    return c-- ? ditto( s, r += s, c ) : r;
}

ditto( "foo", "", 128 );

Saya tidak bisa mengatakan saya terlalu memikirkannya, dan mungkin menunjukkan :-)

Ini bisa dibilang lebih baik

String.prototype.ditto = function( c ) {
    return --c ? this + this.ditto( c ) : this;
};

"foo".ditto( 128 );

Dan ini sangat mirip dengan jawaban yang sudah diposting - Saya tahu ini.

Tetapi mengapa harus rekursif sama sekali?

Dan bagaimana dengan perilaku standar kecil juga?

String.prototype.ditto = function() {
    var c = Number( arguments[ 0 ] ) || 2,
        r = this.valueOf();
    while ( --c ) {
        r += this;
    }
    return r;
}

"foo".ditto();

Karena , meskipun metode non rekursif akan menangani pengulangan besar secara sewenang-wenang tanpa mencapai batas tumpukan panggilan, itu jauh lebih lambat.

Mengapa saya repot-repot menambahkan lebih banyak metode yang tidak sepintar itu yang sudah diposting?

Sebagian untuk hiburan saya sendiri, dan sebagian untuk menunjukkan dengan cara paling sederhana saya tahu bahwa ada banyak cara untuk menguliti kucing, dan tergantung pada situasinya, sangat mungkin bahwa metode yang tampaknya terbaik tidak ideal.

Metode yang relatif cepat dan canggih secara efektif bisa crash dan terbakar dalam keadaan tertentu, sementara metode yang lebih lambat, lebih sederhana dapat menyelesaikan pekerjaan - pada akhirnya.

Beberapa metode mungkin sedikit lebih dari eksploitasi, dan karena itu cenderung ada tetap luar keberadaan, dan metode lain dapat bekerja dengan indah dalam semua kondisi, tetapi begitu dibangun sehingga orang hanya tidak tahu cara kerjanya.

"Jadi bagaimana jika aku tidak tahu cara kerjanya ?!"

Serius?

JavaScript menderita salah satu kekuatan terbesarnya; itu sangat toleran terhadap perilaku buruk, dan sangat fleksibel sehingga akan membungkuk ke belakang untuk mengembalikan hasil, ketika itu mungkin lebih baik untuk semua orang jika itu patah!

"Dengan kekuatan besar, datang tanggung jawab besar" ;-)

Tetapi yang lebih serius dan penting, meskipun pertanyaan umum seperti ini memang mengarah pada kehebatan dalam bentuk jawaban cerdas bahwa jika tidak ada yang lain, perluas pengetahuan dan wawasan Anda, pada akhirnya, tugas yang ada - skrip praktis yang menggunakan metode yang dihasilkan - mungkin membutuhkan sedikit lebih sedikit, atau sedikit lebih pintar dari yang disarankan.

Ini "sempurna" algoritma yang menyenangkan dan semua, tapi "satu ukuran cocok untuk semua" akan jarang jika pernah lebih baik dari tailor made.

Khotbah ini disampaikan kepada Anda karena kurang tidur dan minat yang lewat. Majulah dan kode!

Fred Gandt
sumber
1

Pertama, pertanyaan OP tampaknya tentang keringkasan - yang saya pahami berarti "sederhana dan mudah dibaca", sementara sebagian besar jawaban tampaknya tentang efisiensi - yang jelas bukan hal yang sama dan saya juga berpikir bahwa kecuali Anda menerapkan beberapa khusus algoritma manipulasi data besar, tidak perlu khawatir Anda ketika Anda datang untuk mengimplementasikan fungsi Javascript manipulasi data dasar. Keringkasan jauh lebih penting.

Kedua, seperti yang dicatat André Laszlo, String.repeat adalah bagian dari ECMAScript 6 dan sudah tersedia di beberapa implementasi populer - jadi implementasi yang paling ringkas String.repeatadalah bukan untuk mengimplementasikannya ;-)

Terakhir, jika Anda perlu mendukung host yang tidak menawarkan implementasi ECMAScript 6, polyfill MDN yang disebutkan oleh André Laszlo sama sekali tidak ringkas.

Jadi, tanpa basa-basi lagi - inilah polyfill ringkas saya :

String.prototype.repeat = String.prototype.repeat || function(n){
    return n<=1 ? this : this.concat(this.repeat(n-1));
}

Ya, ini adalah rekursi. Saya suka rekursi - rekursi sederhana dan jika dilakukan dengan benar mudah dimengerti. Mengenai efisiensi, jika bahasa mendukungnya mereka bisa sangat efisien jika ditulis dengan benar.

Dari tes saya, metode ini ~ 60% lebih cepat dari Array.joinpendekatan. Meskipun jelas-jelas tidak ada implementasi disfated dekat, itu jauh lebih sederhana daripada keduanya.

Setup pengujian saya adalah simpul v0.10, menggunakan "mode Ketat" (Saya pikir ini memungkinkan semacam TCO ), memanggil repeat(1000)string 10 karakter sejuta kali.

Guss
sumber
1

Jika Anda menganggap semua definisi prototipe, kreasi array, dan operasi gabungan ini berlebihan, cukup gunakan kode baris tunggal di mana Anda memerlukannya. String S berulang N kali:

for (var i = 0, result = ''; i < N; i++) result += S;
Semra
sumber
3
Kode harus dapat dibaca. Jika Anda benar-benar hanya akan menggunakannya sekali, maka formatlah dengan benar (atau gunakan Array(N + 1).join(str)metode ini jika itu bukan hambatan kinerja). Jika ada kesempatan sekecil apa pun Anda akan menggunakannya dua kali, pindahkan ke fungsi yang sesuai namanya.
cloudfeet
1

Gunakan Lodash untuk fungsionalitas utilitas Javascript, seperti string berulang.

Lodash memberikan kinerja yang bagus dan kompatibilitas ECMAScript.

Saya sangat merekomendasikannya untuk pengembangan UI dan juga berfungsi dengan baik di sisi server.

Inilah cara mengulangi string "yo" 2 kali menggunakan Lodash:

> _.repeat('yo', 2)
"yoyo"
l3x
sumber
0

Solusi rekursif menggunakan membagi dan menaklukkan:

function repeat(n, s) {
    if (n==0) return '';
    if (n==1 || isNaN(n)) return s;
    with(Math) { return repeat(floor(n/2), s)+repeat(ceil(n/2), s); }
}
Fordi
sumber
0

Saya datang ke sini secara acak dan tidak pernah punya alasan untuk mengulangi char di javascript sebelumnya.

Saya terkesan dengan cara artistoex melakukannya dan hasil yang dihina. Saya perhatikan bahwa senar string terakhir tidak perlu, seperti Dennis juga tunjukkan.

Saya memperhatikan beberapa hal lagi ketika bermain dengan sampling disfated disatukan.

Hasilnya bervariasi dalam jumlah yang cukup sering menguntungkan untuk yang terakhir berjalan dan algoritma yang sama sering joki untuk posisi. Salah satu hal yang saya ubah adalah alih-alih menggunakan jumlah yang dihasilkan JSLitmus sebagai benih untuk panggilan; karena hitungan dihasilkan berbeda untuk berbagai metode, saya memasukkan indeks. Ini membuat benda itu jauh lebih dapat diandalkan. Saya kemudian melihat untuk memastikan bahwa berbagai ukuran string diteruskan ke fungsi. Ini mencegah beberapa variasi yang saya lihat, di mana beberapa algoritma melakukan lebih baik pada karakter tunggal atau string yang lebih kecil. Namun 3 metode teratas semuanya bekerja dengan baik terlepas dari ukuran string.

Set uji bercabang

http://jsfiddle.net/schmide/fCqp3/134/

// repeated string
var string = '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789';
// count paremeter is changed on every test iteration, limit it's maximum value here
var maxCount = 200;

var n = 0;
$.each(tests, function (name) {
    var fn = tests[name];
    JSLitmus.test(++n + '. ' + name, function (count) {
        var index = 0;
        while (count--) {
            fn.call(string.slice(0, index % string.length), index % maxCount);
            index++;
        }
    });
    if (fn.call('>', 10).length !== 10) $('body').prepend('<h1>Error in "' + name + '"</h1>');
});

JSLitmus.runAll();

Saya kemudian memasukkan perbaikan Dennis dan memutuskan untuk melihat apakah saya bisa menemukan cara untuk sedikit lebih banyak.

Karena javascript tidak dapat mengoptimalkan hal-hal, cara terbaik untuk meningkatkan kinerja adalah dengan menghindari hal-hal secara manual. Jika saya mengambil 4 hasil pertama yang sepele dari loop, saya bisa menghindari 2-4 toko string dan menulis toko akhir langsung ke hasilnya.

// final: growing pattern + prototypejs check (count < 1)
'final avoid': function (count) {
    if (!count) return '';
    if (count == 1) return this.valueOf();
    var pattern = this.valueOf();
    if (count == 2) return pattern + pattern;
    if (count == 3) return pattern + pattern + pattern;
    var result;
    if (count & 1) result = pattern;
    else result = '';
    count >>= 1;
    do {
        pattern += pattern;
        if (count & 1) result += pattern;
        count >>= 1;
    } while (count > 1);
    return result + pattern + pattern;
}

Ini menghasilkan peningkatan 1-2% rata-rata di atas perbaikan Dennis. Namun, proses yang berbeda dan browser yang berbeda akan menunjukkan varian yang cukup adil sehingga kode tambahan ini mungkin tidak sepadan dengan usaha dari 2 algoritma sebelumnya.

Sebuah Grafik

Sunting: Saya melakukan ini sebagian besar di bawah chrome. Firefox dan IE akan sering menguntungkan Dennis beberapa%.

Andrew Hallendorff
sumber
0

Metode sederhana:

String.prototype.repeat = function(num) {
    num = parseInt(num);
    if (num < 0) return '';
    return new Array(num + 1).join(this);
}
Eduardo Cuomo
sumber
0

Orang terlalu rumit ini sampai pada tingkat yang konyol atau menyia-nyiakan kinerja. Array? Pengulangan? Anda pasti bercanda.

function repeat (string, times) {
  var result = ''
  while (times-- > 0) result += string
  return result
}

Edit. Saya menjalankan beberapa tes sederhana untuk membandingkan dengan versi bitwise yang diposting oleh artistoex / disfated dan sekelompok orang lain. Yang terakhir hanya sedikit lebih cepat, tetapi urutan besarnya lebih hemat memori. Untuk 10.00000 pengulangan kata 'blah', proses Node naik menjadi 46 megabita dengan algoritma penyatuan sederhana (di atas), tetapi hanya 5,5 megabita dengan algoritma logaritmik. Yang terakhir jelas cara untuk pergi. Pengeposan ulang demi kejelasan:

function repeat (string, times) {
  var result = ''
  while (times > 0) {
    if (times & 1) result += string
    times >>= 1
    string += string
  }
  return result
}
Nelo Mitranim
sumber
Anda memiliki string += stringseparuh waktu yang berlebihan .
nikolay
0

String penggabungan berdasarkan pada angka.

function concatStr(str, num) {
   var arr = [];

   //Construct an array
   for (var i = 0; i < num; i++)
      arr[i] = str;

   //Join all elements
   str = arr.join('');

   return str;
}

console.log(concatStr("abc", 3));

Semoga itu bisa membantu!

loxsat
sumber
0

Dengan ES8 Anda juga dapat menggunakan padStartatau padEnduntuk ini. misalnya.

var str = 'cat';
var num = 23;
var size = str.length * num;
"".padStart(size, str) // outputs: 'catcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcat'
wizzfizz94
sumber