الحوسبة الكمومية في الألعاب ، أو الجنون بجدية

إذا كنت تعيش بين الجنون ، فيجب أن تتعلم أن تكون مجنونًا بنفسك

هل سبق لك أن حاولت "تعلم أن تكون مجنون"؟ مهمة غير تافهة. حتى أنك لن تجد أسلوبًا عاديًا ، لأن الجميع يشعرون بالجنون بطريقته الخاصة. محاولتي الأولى: نظرية المؤامرة. لا تتضمن النظرية ممارسة ، مما يعني أنه ليس عليك العمل بجد. مرة أخرى ، في أي حالة ، لن يعاني أحد.

كيف تصنع نظريات المؤامرة؟
إنشاء نظرية المؤامرة بسيط نسبيًا. نحن بحاجة إلى فكرة بسيطة بما يكفي لتكون مقبولة من قبل 90 ٪ من السكان. يجب أن يكون مثيرًا للجدل حتى يتمكن 5٪ من السكان من شرح 90٪ من الأغبياء. أخيرًا ، نحتاج إلى بعض الأبحاث التي لا يفهمها 95٪ من الأشخاص ، لكن يتم استخدامها بنسبة 90٪ كحجة "أثبت الناس أنهم أكثر ذكاءً منا ...".

الحوسبة الكمومية هي مجال كبير لمثل هذه الدراسة. يمكنك إعداد مخطط بسيط ، لكن كلمة "الكم" ستضيف وزناً إلى النتائج.

الهدف من الدراسة هو لعبة ، لأن الكائن يرجع إلى الشباب البسيط والمألوف. من يشارك في الحوسبة الكمومية والألعاب؟ جوجل

لذلك ، النظرية الهرطية: بعد 5 سنوات ، ستقرر بيج وغرين من سيكون الشيء الرئيسي في جوجل ، وسوف يفعلون ذلك بمساعدة اللعبة. كل واحد منهم لديه مجموعة من الباحثين. فريق AlphaGo مع شبكاتهم العصبية القتالية سحبت المنافسين في Go. أُجبر المعارضون على البحث عن طرق جديدة ، وما زالوا يجدون أداة التفوق التام : الحوسبة الكمومية.

هل يمكنني استخدام Quantum Computing للألعاب؟ سهل دعونا نوضح على سبيل المثال أن لعبة "صياد الثعلب" يمكن حلها في 6 حركات. من أجل المصداقية ، سنقصر أنفسنا على 15 qubits (لا يقلد المحرر عبر الإنترنت لأكثر من خمسة عشر) ، من أجل البساطة ، سوف نتجاهل القيود المفروضة على بنية المعالج وتصحيح الخطأ.

القواعد


بسيط للغاية. هناك خمسة ثقوب مرتبة في صف واحد (نعددها كـ 0-1-2-3-4). في واحد منهم هو الثعلب. كل ليلة ، ينتقل الثعلب إلى المنك التالي يسارًا أو يمينًا. كل صباح ، يمكن للصياد فحص ثقب واحد للاختيار من بينها. مهمة الصياد هي التقاط الثعلب. مهمة الثعلب هي البقاء على قيد الحياة. من الناحية النظرية ، يمكن أن يهرب الثعلب من صياد إلى الأبد. في الممارسة العملية ، هناك استراتيجية رابحة: فحص الثقوب 1-2-3-1-2-3. فقط هذه الاستراتيجية سوف أختبر.

بناء مخطط


دعنا نبدأ مع بدء qubits 0-1-2-3-4 (5 ثقوب). هنا يمكنك تعديل


في الواقع ، بعد البدء ، لدينا نظام يكون فيه ، بعد القياس ، صاروخ واحد بدقة. تختلف احتمالات "الوحدة" بالنسبة لكل qubit ، لكن في حالتنا هذه ليست حرجة. يجب أن نترك المجال لمناقشة المخطط (ونظريتنا في نفس الوقت).

في Q # ، حصلنا على رمز مثل هذا:

operation TestStrategy () : (Result) { let res = Zero; using(qubits=Qubit[16]) { // 0..4 - holes // 5 - current movement direction. Zero means "go down", One means "go up" // 6 - Game status. 1 means "fox is free, go further" // 7,8,9,10, 11 - movements history InitFoxHoles(qubits); ResetAll(qubits); // ALWAYS clean after yourself } return Zero; } // Inits fox holes, with almost equal probabilities operation InitFoxHoles(register: Qubit[]) : Unit { body { ResetAll(register); // Step 1 H(register[0]); H(register[2]); // Step 2 (Controlled (X))([register[0],register[2]], register[3]); // Step 3 X(register[0]); X(register[2]); (Controlled (X))([register[0],register[2]], register[3]); X(register[0]); X(register[2]); // Step 4 CNOT(register[3], register[0]); CNOT(register[3], register[2]); // Step 5 (Controlled (H))([register[3]], register[4]); // Step 6 CNOT(register[4], register[3]); } } 

سوف TestStrategy اختبار استراتيجيتنا 1-2-3-1-2-3 ، InitFoxHoles () مسؤولة فقط عن بدء ثقوب الثعلب. دعونا التحقق من البدء. انسخ TestStrategy ، وابدأ التهيئة ، وقم بقياس أول 5 بتات وإرجاع قيمها.

  operation TestInit(): (Result, Result, Result, Result, Result) { body { mutable res0 = Zero; mutable res1 = Zero; mutable res2 = Zero; mutable res3 = Zero; mutable res4 = Zero; using(qubits=Qubit[16]) { // 0..4 - holes // 5 - current movement direction. Zero means "go down", One means "go up" // 6 - Game status. 1 means "fox is free, go further" // 7,8,9,10, 11 - movements history InitFoxHoles(qubits); set res0 = M(qubits[0]); set res1 = M(qubits[1]); set res2 = M(qubits[2]); set res3 = M(qubits[3]); set res4 = M(qubits[4]); ResetAll(qubits); // ALWAYS clean after yourself } return (res0, res1, res2, res3, res4); } } 

سنقوم بإجراء الاختبار ألف مرة (تعد عمليات التشغيل المتعددة نموذجية للخوارزميات الكمية ، حتى في بعض الأماكن تكون ضرورية). رمز الاتصال - تحت المفسد ، النتائج: على الشاشة أدناه.

اختبار بسرعة البدء
  static void TestInitiation() { using (var sim = new QuantumSimulator()) { var initedQubitsValues = Enumerable.Range(0, 5) .ToDictionary(qubitIndex => qubitIndex, oneMesaured => 0); for (int i = 0; i < 1000; i++) { (Result, Result, Result, Result, Result) result = TestInit.Run(sim).Result; if (result.Item1 == Result.One) { initedQubitsValues[0]++; } if (result.Item2 == Result.One) { initedQubitsValues[1]++; } if (result.Item3 == Result.One) { initedQubitsValues[2]++; } if (result.Item4 == Result.One) { initedQubitsValues[3]++; } if (result.Item5 == Result.One) { initedQubitsValues[4]++; } } Console.WriteLine($"Qubit-0 initiations: {initedQubitsValues[0]}"); Console.WriteLine($"Qubit-1 initiations: {initedQubitsValues[1]}"); Console.WriteLine($"Qubit-2 initiations: {initedQubitsValues[2]}"); Console.WriteLine($"Qubit-3 initiations: {initedQubitsValues[3]}"); Console.WriteLine($"Qubit-4 initiations: {initedQubitsValues[4]}"); } } 




حدث خطأ ما. كان من المتوقع توزيع موحد تقريبا. السبب بسيط: في الخطوة 3 ، قمت بعكس البادئة الثالثة ، بدلاً من الأولى: (Controlled (X)) ([تسجيل [0] ، تسجيل [2]] ، تسجيل [3]) ؛ ليست جيدة نسخة لصق القديم.

نصلح الكود ونجري الاختبار:

بدء تصحيح
  // Inits fox holes, with almost equal probabilities operation InitFoxHoles(register: Qubit[]) : Unit { body { ResetAll(register); // Step 1 H(register[0]); H(register[2]); // Step 2 (Controlled (X))([register[0],register[2]], register[3]); // Step 3 X(register[0]); X(register[2]); (Controlled (X))([register[0],register[2]], register[1]); X(register[0]); X(register[2]); // Step 4 CNOT(register[3], register[0]); CNOT(register[3], register[2]); // Step 5 (Controlled (H))([register[3]], register[4]); // Step 6 CNOT(register[4], register[3]); } } } 




بالفعل أفضل. يمكن رؤية الرمز في اللفت ، الإصدار Commit 1 .

حيث لتشغيل الثعلب؟


حدد البادئة الخامسة (يبدأ الترقيم من الأعلى) تحت الاتجاه الحالي للثعلب. نحن نتفق على أن الصفر يعني حركة هبوطية ، وحدة تعني حركة تصاعدية. من الواضح ، إذا كان الثعلب بالفعل في حفرة الصفر - يجب أن تتحرك لأسفل. إذا كان الثعلب في الفتحة الرابعة ، فإنه يتحرك للأعلى. في حالات أخرى ، يمكن أن يتحرك الثعلب لأعلى ولأسفل. وفقًا لهذه القواعد البسيطة ، يمكننا ضبط "نصف اتجاه الاتجاه الحالي" على 0 ، 1 ، أو تراكب صفري وواحد. ننظر إلى الكود في المستودع ، الالتزام 2 .


مخطط في المحرر.

الرمز والاختبار
 // Select next Fox movement direction, updating qubit 5 // 1 means go up (4 -> 3, 3 -> 2, ... 1 -> 0) // 0 means go down (0 -> 1, 1 -> 2, ... 3 -> 4) operation SetupMovementDirection(qubits: Qubit[]) : Unit { body { // Step 1 CNOT(qubits[4], qubits[5]); // Step 2 (Controlled (H))([qubits[3]], qubits[5]); // Step 3 (Controlled (H))([qubits[2]], qubits[5]); // Step 4 (Controlled (H))([qubits[1]], qubits[5]); } } operation TestMovementDirectionSetup(): (Result, Result, Result, Result, Result, Result) { body { mutable res0 = Zero; mutable res1 = Zero; mutable res2 = Zero; mutable res3 = Zero; mutable res4 = Zero; mutable res5 = Zero; using(qubits=Qubit[16]) { InitFoxHoles(qubits); SetupMovementDirection(qubits); set res0 = M(qubits[0]); set res1 = M(qubits[1]); set res2 = M(qubits[2]); set res3 = M(qubits[3]); set res4 = M(qubits[4]); set res5 = M(qubits[5]); ResetAll(qubits); // ALWAYS clean after yourself } return (res0, res1, res2, res3, res4, res5); } } 


  static void TestMovementDirectionSetup() { using (var sim = new QuantumSimulator()) { List<string> results = new List<string>(); string initedCubit = null; string moveDirection = null; for (int i = 0; i < 1000; i++) { (Result, Result, Result, Result, Result, Result) result = Quantum.FoxHunter.TestMovementDirectionSetup.Run(sim).Result; if (result.Item1 == Result.One) { initedCubit = "0"; } if (result.Item2 == Result.One) { initedCubit = "1"; } if (result.Item3 == Result.One) { initedCubit = "2"; } if (result.Item4 == Result.One) { initedCubit = "3"; } if (result.Item5 == Result.One) { initedCubit = "4"; } if (result.Item6 == Result.One) { moveDirection = "1"; } else { moveDirection = "0"; } results.Add($"{initedCubit}{moveDirection}"); } foreach(var group in results .GroupBy(result => result) .OrderBy(group => group.Key)) { Console.WriteLine($"{group.Key} was measured {group.Count()} times"); } Console.WriteLine($"\r\nTotal measures: {results.Count()}"); } } 





الحركة

تنفذ بواسطة SWAP التي تسيطر عليها. إذا كان البادئة المسيطرة أحادية المقايضة. إذا كان معدل الدوران المتحكم هو صفر ، فإننا نقوم بالمبادلة.


مخطط في المحرر .

س # المشغل
  // Makes a movement based on the 5'th qubit value // 1 means go up (4 -> 3, 3 -> 2, ... 1 -> 0) // 0 means go down (0 -> 1, 1 -> 2, ... 3 -> 4) operation MakeMovement(qubits: Qubit[]) : Unit { body { // Code movement Up // Step 1 mutable qubitsToSwap = [qubits[0], qubits[1]]; (Controlled(SwapReverseRegister))([qubits[5]],qubitsToSwap); // Step 2 set qubitsToSwap = [qubits[1], qubits[2]]; (Controlled(SwapReverseRegister))([qubits[5]],qubitsToSwap); // Step 3 set qubitsToSwap = [qubits[2], qubits[3]]; (Controlled(SwapReverseRegister))([qubits[5]],qubitsToSwap); // Step 4 set qubitsToSwap = [qubits[3], qubits[4]]; (Controlled(SwapReverseRegister))([qubits[5]],qubitsToSwap); // COde movement down X(qubits[5]); // Invert direction qubit for the ZeroControlled operations // Step 5 set qubitsToSwap = [qubits[3], qubits[4]]; (Controlled(SwapReverseRegister))([qubits[5]],qubitsToSwap); // Step 6 set qubitsToSwap = [qubits[2], qubits[3]]; (Controlled(SwapReverseRegister))([qubits[5]],qubitsToSwap); // Step 7 set qubitsToSwap = [qubits[1], qubits[2]]; (Controlled(SwapReverseRegister))([qubits[5]],qubitsToSwap); // Step 8 set qubitsToSwap = [qubits[0], qubits[1]]; (Controlled(SwapReverseRegister))([qubits[5]],qubitsToSwap); X(qubits[5]); // Back-invert for the direction qubit } } 


س #: بيان للاختبارات
  operation TestFirstMovement(): (Result, Result, Result, Result, Result, Result) { body { mutable res0 = Zero; mutable res1 = Zero; mutable res2 = Zero; mutable res3 = Zero; mutable res4 = Zero; mutable res5 = Zero; using(qubits=Qubit[16]) { InitFoxHoles(qubits); SetupMovementDirection(qubits); MakeMovement(qubits); set res0 = M(qubits[0]); set res1 = M(qubits[1]); set res2 = M(qubits[2]); set res3 = M(qubits[3]); set res4 = M(qubits[4]); set res5 = M(qubits[5]); ResetAll(qubits); // ALWAYS clean after yourself } return (res0, res1, res2, res3, res4, res5); } } 


رمز C #
  static void TestFirstMove() { using (var sim = new QuantumSimulator()) { List<string> results = new List<string>(); string initedCubit = null; string moveDirection = null; for (int i = 0; i < 1000; i++) { (Result, Result, Result, Result, Result, Result) result = Quantum.FoxHunter.TestFirstMovement.Run(sim).Result; if (result.Item1 == Result.One) { initedCubit = "0"; } if (result.Item2 == Result.One) { initedCubit = "1"; } if (result.Item3 == Result.One) { initedCubit = "2"; } if (result.Item4 == Result.One) { initedCubit = "3"; } if (result.Item5 == Result.One) { initedCubit = "4"; } if (result.Item6 == Result.One) { moveDirection = "1"; } else { moveDirection = "0"; } results.Add($"{initedCubit}{moveDirection}"); } // Holes measurements foreach (var group in results .GroupBy(result => result[0]) .OrderBy(group => group.Key)) { Console.WriteLine($"{group.Key} hole was measured {group.Count()} times"); } // Directions measuremetns foreach (var group in results .GroupBy(result => result[1]) .OrderBy(group => group.Key)) { Console.WriteLine($"{group.Key} direction was measured {group.Count()} times"); } Console.WriteLine($"\r\nTotal measures: {results.Count()}"); } } 


يمكن الاطلاع على الكود في الالتزام 3 .

نجعل 6 التحركات


أخيرًا ، نختار qubit السادس لحالة اللعبة (الثعلب مجاني / الثعلب ليس مجاني). الوحدة يتوافق مع الثعلب الحر. سنقوم بإجراء المزيد من التحركات فقط مع وجود حالة واحدة.

سوف qubits 7،8،9،10،11 الاحتفاظ بسجل من التحركات. بعد كل خطوة ، سنقوم بتبديل أحدهما بنقطة في الاتجاه الحالي (هذا سيتيح لنا تخزين تاريخ الحركات وإعادة ضبط معدل البت في الاتجاه الحالي قبل كل حركة).

مخطط المرفقة .

س # المشغل
  /// Make 6 movements. Every movement is controlled by the 6'th qubit. /// After the every qubit we check if the fox has been captured and invert the 6'th qubit /// Reminder: 6'th qubit equal to One means "Fox is free, go further" operation MakeSixMovements(qubits: Qubit[]) : Unit { body { // Move 1 (Controlled(SetupMovementDirection))([qubits[6]],(qubits)); (Controlled(MakeMovement))([qubits[6]],(qubits)); CNOT(qubits[1], qubits[6]); // Reverse Fox State if it's shot // Move 2 SwapReverseRegister([qubits[5], qubits[7]]); // Move the first move direction to the qubit 7, qubit 5 is Zero again (Controlled(SetupMovementDirection))([qubits[6]],(qubits)); (Controlled(MakeMovement))([qubits[6]],(qubits)); CNOT(qubits[2], qubits[6]); // Move 3 SwapReverseRegister([qubits[5], qubits[8]]); (Controlled(SetupMovementDirection))([qubits[6]],(qubits)); (Controlled(MakeMovement))([qubits[6]],(qubits)); CNOT(qubits[3], qubits[6]); // Move 4 SwapReverseRegister([qubits[5], qubits[9]]); (Controlled(SetupMovementDirection))([qubits[6]],(qubits)); (Controlled(MakeMovement))([qubits[6]],(qubits)); CNOT(qubits[1], qubits[6]); // Move 5 SwapReverseRegister([qubits[5], qubits[10]]); (Controlled(SetupMovementDirection))([qubits[6]],(qubits)); (Controlled(MakeMovement))([qubits[6]],(qubits)); CNOT(qubits[2], qubits[6]); // Move 6 SwapReverseRegister([qubits[5], qubits[11]]); (Controlled(SetupMovementDirection))([qubits[6]],(qubits)); (Controlled(MakeMovement))([qubits[6]],(qubits)); CNOT(qubits[3], qubits[6]); } } 


س #: بيان للاختبارات
  operation TestSixMovements(): (Result) { body { mutable res = Zero; using(qubits=Qubit[16]) { ResetAll(qubits); InitFoxHoles(qubits); X(qubits[6]); // At the beginning of the game our fox is alive MakeSixMovements(qubits); set res = M(qubits[6]); ResetAll(qubits); // ALWAYS clean after yourself } return (res); } } 


جيم #: الاختبار
  static void TestMovements() { using (var sim = new QuantumSimulator()) { int zerosCount = 0; for (int i = 0; i < 1000; i++) { Result result = Quantum.FoxHunter.TestSixMovements.Run(sim).Result; if(result == Result.Zero) { zerosCount++; } } Console.WriteLine($"\r\nTotal zeroes: {zerosCount}"); } } 


نحن ننظر الالتزام 4 .

اللمسات الأخيرة


لدينا خطأ في الدائرة. بما أننا نختبر الاستراتيجية 1-2-3-1-2-3 ، فإننا نتحقق من كل حفرة مرتين. وفقًا لذلك ، بعد أن اصطدنا بالثعلب في الخطوة الأولى ، سننتقل إلى حالة الوضع المؤقت مرتين (في الخطوة الأولى والرابعة).

لتجنب هذا الموقف ، نستخدم 12 بتة لإصلاح الحالة بعد التحركات 4-5-6. بالإضافة إلى ذلك ، نضيف تعريف النصر: إذا تحول واحد على الأقل من مثيلات الحالة إلى صفر ، فزنا.

المخطط النهائي .

س #: إصلاح المشغل 6 الخطوة
  operation MakeSixMovements(qubits: Qubit[]) : Unit { body { // Move 1 (Controlled(SetupMovementDirection))([qubits[6]],(qubits)); (Controlled(MakeMovement))([qubits[6]],(qubits)); CNOT(qubits[1], qubits[6]); // Reverse Fox State if it's shot // Move 2 SwapReverseRegister([qubits[5], qubits[7]]); // Move the first move direction to the qubit 7, qubit 5 is Zero again (Controlled(SetupMovementDirection))([qubits[6]],(qubits)); (Controlled(MakeMovement))([qubits[6]],(qubits)); CNOT(qubits[2], qubits[6]); // Move 3 SwapReverseRegister([qubits[5], qubits[8]]); (Controlled(SetupMovementDirection))([qubits[6]],(qubits)); (Controlled(MakeMovement))([qubits[6]],(qubits)); CNOT(qubits[3], qubits[6]); // Move 4 SwapReverseRegister([qubits[5], qubits[9]]); (Controlled(SetupMovementDirection))([qubits[6], qubits[12]],(qubits)); (Controlled(MakeMovement))([qubits[6], qubits[12]],(qubits)); CNOT(qubits[1], qubits[12]); // Move 5 SwapReverseRegister([qubits[5], qubits[10]]); (Controlled(SetupMovementDirection))([qubits[6], qubits[12]],(qubits)); (Controlled(MakeMovement))([qubits[6], qubits[12]],(qubits)); CNOT(qubits[2], qubits[12]); // Move 6 SwapReverseRegister([qubits[5], qubits[11]]); (Controlled(SetupMovementDirection))([qubits[6], qubits[12]],(qubits)); (Controlled(MakeMovement))([qubits[6], qubits[12]],(qubits)); CNOT(qubits[3], qubits[12]); } } 


س #: إصلاح استراتيجية اختبار المشغل 1-2-3-1-2-3
  operation TestStrategy () : (Result) { // 0..4 - holes // 5 - current movement direction. Zero means "go down", One means "go up" // 6 - Game status. 1 means "fox is free, go further" // 7,8,9,10, 11 - movements history // 12 - another qubit of the fox live. 1 means "fox is still free, go further" // 13 Result qubit. If it's zero, the fox is alive body { mutable res = Zero; using(qubits=Qubit[14]) { ResetAll(qubits); // Init fox positions and the fox' live InitFoxHoles(qubits); X(qubits[6]); // At the beginning of the game our fox is alive X(qubits[12]); // The second qubit of the fox live. If it's one - the fox is alive. // Make moves MakeSixMovements(qubits); // Measure results. If the 13'th qubit is zero the fox is alive X(qubits[6]); X(qubits[12]); CNOT(qubits[6], qubits[13]); CNOT(qubits[12], qubits[13]); CCNOT(qubits[6], qubits[12], qubits[13]); set res = M(qubits[13]); ResetAll(qubits); // ALWAYS clean after yourself } return (res); } } 


جيم #: تشغيل الاختيار النهائي
  static void RunFoxHunt() { Stopwatch sw = new Stopwatch(); sw.Start(); using (var sim = new QuantumSimulator()) { var foxSurvives = 0; var hunterWins = 0; for (int i = 0; i < 1000; i++) { var result = (Result)(TestStrategy.Run(sim).Result); if (result == Result.Zero) { foxSurvives++; } else { hunterWins++; } } Console.WriteLine($"Fox survives: \t{foxSurvives}"); Console.WriteLine($"Hunter wins: \t{hunterWins}"); } sw.Stop(); Console.WriteLine($"Experiment finished. " + $"Time spent: {sw.ElapsedMilliseconds / 1000} seconds"); } 



الالتزام 5 .

ما يلي من هذا


من حيث المبدأ ، يمكن تحسين المخطط في كل من عدد البتات وعدد العمليات. يتمثل التحسين الأمثل لعدد البتات في التخلص من الكابت (13) ، والعودة فقط 6 و 12. تحسين العمليات - لجعل الطلقة الأولى مباشرة بعد البدء. ومع ذلك ، دعنا نترك هذا العمل لمهندسي Google.

كما ترون ، يمكن لأي شخص لديه معرفة سطحية بالحوسبة الكمومية أن يلعب بأمان "صياد الثعلب". إذا كان لدينا عدد أكبر قليلاً من وحدات البت ، يمكننا إيجاد الحل الأمثل ، وعدم التحقق من الحل الحالي. من الممكن تمامًا أن يسقط تيك تاك (والنسخة الكمومية) والمدققون والشطرنج والذهاب في المرة التالية.

في الوقت نفسه ، تظل مسألة "قابلية الحل" للألعاب مثل DotA و Starcraft و Doom مفتوحة. بالنسبة إلى الحوسبة الكمومية ، يعد تخزين سجل النقرات بالكامل أمرًا مميزًا. نحن نأخذ APM (الإجراءات في الدقيقة) 500 ، ومضاعفة بعدد اللاعبين ، وضربنا بعدد الدقائق ، ونضيف العشوائية للعبة نفسها - عدد البتات المطلوب لتخزين جميع المعلومات ينمو بسرعة كبيرة.

لذلك ، فإن اختيار لعبة في مسابقة صغيرة بين Brin و Page يمكن أن يلعب دورًا حاسمًا. ومع ذلك ، فإن إنشاء لعبة "صعبة على قدم المساواة" لأجهزة الكمبيوتر الكلاسيكية والكمية يستحق نظريتها الخاصة.

Source: https://habr.com/ru/post/ar437352/


All Articles