主頁 > 後端開發 > 模擬退火演算法Python編程(4)旅行商問題

模擬退火演算法Python編程(4)旅行商問題

2021-05-05 13:46:24 後端開發

1、旅行商問題(Travelling salesman problem, TSP)

  旅行商問題是經典的組合優化問題,要求找到遍歷所有城市且每個城市只訪問一次的最短旅行路線,即對給定的正權完全圖求其總權重最小的Hamilton回路:設有 n個城市和距離矩陣 D=[dij],其中dij表示城市i到城市j的距離(i,j = 1,2 … n),則問題是要找出遍訪每個城市恰好一次的一潭訓路并使其路徑長度為最短,旅行商問題屬于NP完全問題,其全域優化解的計算量以問題規模的階乘關系增長,旅行商問題不僅作為一類典型的組合優化問題經常被用作演算法研究和比較的案例,許多實際應用問題如路徑規劃、交通物流、網路管理也可轉化為旅行商問題,
  目前,旅行商問題的研究主要集中于探索和發展各種高效近似最優解的優化方法,包括基于問題特征資訊(如城市位置、距離、角度等)構造的各種啟發式搜索演算法,以及通過模擬或解釋自然規律而發展的模擬退火演算法、遺傳演算法、蟻群演算法、神經網路演算法等智能優化演算法或將二者相結合的混合演算法,
  模擬退火演算法不僅可以解決連續函式優化問題,KIRKPATRICK在1983年成功將其應用于求解組合優化問題,模擬退火演算法現已成為求解旅行商問題的常用方法,通常采用反序、移位和交換等操作算子產生新解,


2、模擬退火演算法求解旅行商問題

  模擬退火演算法要從當前解的鄰域中產生新的候選解,解的表達形式和鄰域結構對演算法收斂非常重要,組合優化問題不同于函式優化,其自變數不是連續變化的,目標函式不僅依賴于自變數的數值,而且與變數的排列次序有關,極端地,旅行商問題的路徑長度僅取決于排列次序,因此常用城市編號的序列來表示解,
  新解的產生機制是在當前解序列的基礎上進行變換操作,隨機改變序列中某些點的排列次序,常見的基本變換操作有交換算子(Swap Operator)、反序算子(Inversion Operator)、移位算子(Move Operator)等,交換算子將當前路徑 S_now 中的第 i 個城市 Ci 與第 j 個城市 Cj 的位置交換;反序算子也稱2-opt,將當前路徑 S_now 中從第 i 個城市 Ci 到第 j 個城市 Cj 之間的城市排列順序反向翻轉;移位算子相當于 Or-opt 操作t,將當前路徑 S_now 中的第 i 個城市 Ci 移動到第 j 個城市 Cj 之后的位置,


3、 程式說明

  下段給出了模擬退火演算法求解旅行商問題的 Python程式,對于程式中的一些細節處理,說明如下:

  1. 資料的獲取,為了避免讓讀者下載資料檔案,程式中采用直接賦值方式讀取旅行城市位置的坐標,實際上,通常是使用資料檔案給出城市位置的引數,程式中已經給出了讀取 TSPLib(旅行商問題國際標準資料集)的子程式和呼叫方法,
  2. 城市間距的計算,按照 TSPLib 的處理規范,一是城市間距按歐式距離計算,不要說地球是圓的要算球面間距了;二是對計算的城市間距離取整(四舍五入),而不是用實數表示城市間距離,這會導致一些優化結果的差異,
  3. 新路徑的產生,為便于理解,本程式只給出了交換算子(Swap Operator)的子程式,交換操作非常容易理解,但實際上優化效率和效果并不好,反序操作的性能顯著優于交換算子,

4、模擬退火演算法求解旅行商問題 Python 程式

# 模擬退火演算法求解旅行商問題 Python程式
# Program: SimulatedAnnealing_v6.py
# Purpose: Simulated annealing algorithm for traveling salesman problem
# v1.0:
#   模擬退火求解旅行商問題(TSP)基本演算法
# Copyright 2021 YouCans, XUPT
# Crated:2021-05-01

#  -*- coding: utf-8 -*-
import math                         # 匯入模塊 math
import random                       # 匯入模塊 random
import pandas as pd                 # 匯入模塊 pandas 并簡寫成 pd
import numpy as np                  # 匯入模塊 numpy 并簡寫成 np YouCans
import matplotlib.pyplot as plt     # 匯入模塊 matplotlib.pyplot 并簡寫成 plt

np.set_printoptions(precision=4)
pd.set_option('display.max_rows', 20)
pd.set_option('expand_frame_repr', False)
pd.options.display.float_format = '{:,.2f}'.format

# 子程式:初始化模擬退火演算法的控制引數
def initParameter():
    # custom function initParameter():
    # Initial parameter for simulated annealing algorithm
    tInitial = 100.0                # 設定初始退火溫度(initial temperature)
    tFinal  = 1                     # 設定終止退火溫度(stop temperature)
    nMarkov = 1000                # Markov鏈長度,也即內回圈運行次數
    alfa    = 0.98                 # 設定降溫引數,T(k)=alfa*T(k-1)

    return tInitial,tFinal,alfa,nMarkov

# 子程式:讀取TSPLib資料
def read_TSPLib(fileName):
    # custom function read_TSPLib(fileName)
    # Read datafile *.dat from TSPlib
    # return coordinates of each city by YouCans, XUPT

    res = []
    with open(fileName, 'r') as fid:
        for item in fid:
            if len(item.strip())!=0:
                res.append(item.split())

    loadData = https://www.cnblogs.com/youcans/p/np.array(res).astype('int')      # 資料格式:i Xi Yi
    coordinates = loadData[:,1::]
    return coordinates

# 子程式:計算各城市間的距離,得到距離矩陣
def getDistMat(nCities, coordinates):
    # custom function getDistMat(nCities, coordinates):
    # computer distance between each 2 Cities
    distMat = np.zeros((nCities,nCities))       # 初始化距離矩陣
    for i in range(nCities):
        for j in range(i,nCities):
            # np.linalg.norm 求向量的范數(默認求 二范數),得到 i、j 間的距離
            distMat[i][j] = distMat[j][i] = round(np.linalg.norm(coordinates[i]-coordinates[j]))
    return distMat                              # 城市間距離取整(四舍五入)

# 子程式:計算 TSP 路徑長度
def calTourMileage(tourGiven, nCities, distMat):
    # custom function caltourMileage(nCities, tour, distMat):
    # to compute mileage of the given tour
    mileageTour = distMat[tourGiven[nCities-1], tourGiven[0]]   # dist((n-1),0)
    for i in range(nCities-1):                                  # dist(0,1),...dist((n-2)(n-1))
        mileageTour += distMat[tourGiven[i], tourGiven[i+1]]
    return round(mileageTour)                     # 路徑總長度取整(四舍五入)

# 子程式:繪制 TSP 路徑圖
def plot_tour(tour, value, coordinates):
    # custom function plot_tour(tour, nCities, coordinates)

    num = len(tour)
    x0, y0 = coordinates[tour[num - 1]]
    x1, y1 = coordinates[tour[0]]
    plt.scatter(int(x0), int(y0), s=15, c='r')      # 繪制城市坐標點 C(n-1)
    plt.plot([x1, x0], [y1, y0], c='b')             # 繪制旅行路徑 C(n-1)~C(0)
    for i in range(num - 1):
        x0, y0 = coordinates[tour[i]]
        x1, y1 = coordinates[tour[i + 1]]
        plt.scatter(int(x0), int(y0), s=15, c='r')  # 繪制城市坐標點 C(i)
        plt.plot([x1, x0], [y1, y0], c='b')         # 繪制旅行路徑 C(i)~C(i+1)

    plt.xlabel("Total mileage of the tour:{:.1f}".format(value))
    plt.title("Optimization tour of TSP{:d}".format(num))  # 設定圖形標題
    plt.show()

# 子程式:交換操作算子
def mutateSwap(tourGiven, nCities):
    # custom function mutateSwap(nCities, tourNow)
    # produce a mutation tour with 2-Swap operator
    # swap the position of two Cities in the given tour

    # 在 [0,n) 產生 2個不相等的隨機整數 i,j
    i = np.random.randint(nCities)          # 產生第一個 [0,n) 區間內的隨機整數
    while True:
        j = np.random.randint(nCities)      # 產生一個 [0,n) 區間內的隨機整數
        if i!=j: break                      # 保證 i, j 不相等

    tourSwap = tourGiven.copy()             # 將給定路徑復制給新路徑 tourSwap
    tourSwap[i],tourSwap[j] = tourGiven[j],tourGiven[i] # 交換 城市 i 和 j 的位置————簡潔的實作方法

    return tourSwap

def main():
    # 主程式

    # # 讀取旅行城市位置的坐標
    coordinates = np.array([[565.0, 575.0], [25.0, 185.0], [345.0, 750.0], [945.0, 685.0], [845.0, 655.0],
                            [880.0, 660.0], [25.0, 230.0], [525.0, 1000.0], [580.0, 1175.0], [650.0, 1130.0],
                            [1605.0, 620.0], [1220.0, 580.0], [1465.0, 200.0], [1530.0, 5.0], [845.0, 680.0],
                            [725.0, 370.0], [145.0, 665.0], [415.0, 635.0], [510.0, 875.0], [560.0, 365.0],
                            [300.0, 465.0], [520.0, 585.0], [480.0, 415.0], [835.0, 625.0], [975.0, 580.0],
                            [1215.0, 245.0], [1320.0, 315.0], [1250.0, 400.0], [660.0, 180.0], [410.0, 250.0],
                            [420.0, 555.0], [575.0, 665.0], [1150.0, 1160.0], [700.0, 580.0], [685.0, 595.0],
                            [685.0, 610.0], [770.0, 610.0], [795.0, 645.0], [720.0, 635.0], [760.0, 650.0],
                            [475.0, 960.0], [95.0, 260.0], [875.0, 920.0], [700.0, 500.0], [555.0, 815.0],
                            [830.0, 485.0], [1170.0, 65.0], [830.0, 610.0], [605.0, 625.0], [595.0, 360.0],
                            [1340.0, 725.0], [1740.0, 245.0]])
    # fileName = "../data/eil76.dat"                      # 資料檔案的地址和檔案名
    # coordinates = read_TSPLib(fileName)                 # 呼叫子程式,讀取城市坐標資料檔案

    # 模擬退火演算法引數設定
    tInitial,tFinal,alfa,nMarkov = initParameter()      # 呼叫子程式,獲得設定引數

    nCities = coordinates.shape[0]              # 根據輸入的城市坐標 獲得城市數量 nCities
    distMat = getDistMat(nCities, coordinates)  # 呼叫子程式,計算城市間距離矩陣
    nMarkov = nCities                           # Markov鏈 的初值設定
    tNow    = tInitial                          # 初始化 當前溫度(current temperature)

    # 初始化準備
    tourNow   = np.arange(nCities)   # 產生初始路徑,回傳一個初值為0、步長為1、長度為n 的排列
    valueNow  = calTourMileage(tourNow,nCities,distMat) # 計算當前路徑的總長度 valueNow
    tourBest  = tourNow.copy()                          # 初始化最優路徑,復制 tourNow
    valueBest = valueNow                                # 初始化最優路徑的總長度,復制 valueNow
    recordBest = []                                     # 初始化 最優路徑記錄表
    recordNow  = []                                     # 初始化 最優路徑記錄表

    # 開始模擬退火優化程序
    iter = 0                        # 外回圈迭代次數計數器
    while tNow >= tFinal:           # 外回圈,直到當前溫度達到終止溫度時結束
        # 在當前溫度下,進行充分次數(nMarkov)的狀態轉移以達到熱平衡

        for k in range(nMarkov):    # 內回圈,回圈次數為Markov鏈長度
            # 產生新解:
            tourNew = mutateSwap(tourNow, nCities)      # 通過 交換操作 產生新路徑
            # tourNew,deltaE = mutateSwapE(tourNow,nCities,distMat)   # 通過 交換操作 產生新路徑(計算 deltaE)
            valueNew = calTourMileage(tourNew,nCities,distMat) # 計算當前路徑的總長度
            deltaE = valueNew - valueNow

            # 接受判別:按照 Metropolis 準則決定是否接受新解
            if deltaE < 0:                          # 更優解:如果新解的目標函式好于當前解,則接受新解
                accept = True
                if valueNew < valueBest:            # 如果新解的目標函式好于最優解,則將新解保存為最優解
                    tourBest[:] = tourNew[:]
                    valueBest = valueNew
            else:                                   # 容忍解:如果新解的目標函式比當前解差,則以一定概率接受新解
                pAccept = math.exp(-deltaE/tNow)    # 計算容忍解的狀態遷移概率
                if pAccept > random.random():
                    accept = True
                else:
                    accept = False

            # 保存新解
            if accept == True:                      # 如果接受新解,則將新解保存為當前解
                tourNow[:] = tourNew[:]
                valueNow = valueNew

        # 平移當前路徑,以解決變換操作避開 0,(n-1)所帶來的問題,并未實質性改變當前路徑,
        tourNow = np.roll(tourNow,2)                # 回圈移位函式,沿指定軸滾動陣列元素

        # 完成當前溫度的搜索,保存資料和輸出
        recordBest.append(valueBest)                # 將本次溫度下的最優路徑長度追加到 最優路徑記錄表
        recordNow.append(valueNow)                  # 將當前路徑長度追加到 當前路徑記錄表
        print('i:{}, t(i):{:.2f}, valueNow:{:.1f}, valueBest:{:.1f}'.format(iter,tNow,valueNow,valueBest))

        # 緩慢降溫至新的溫度,
        iter = iter + 1
        tNow = tNow * alfa                              # 指數降溫曲線:T(k)=alfa*T(k-1)

    # 結束模擬退火程序

    # 圖形化顯示優化結果
    figure1 = plt.figure()     # 創建圖形視窗 1
    plot_tour(tourBest, valueBest, coordinates)
    figure2 = plt.figure()     # 創建圖形視窗 2
    plt.title("Optimization result of TSP{:d}".format(nCities)) # 設定圖形標題
    plt.plot(np.array(recordBest),'b-', label='Best')           # 繪制 recordBest曲線
    plt.plot(np.array(recordNow),'g-', label='Now')             # 繪制 recordNow曲線
    plt.xlabel("iter")                                          # 設定 x軸標注
    plt.ylabel("mileage of tour")                               # 設定 y軸標注
    plt.legend()                                                # 顯示圖例
    plt.show()

    print("Tour verification successful!")
    print("Best tour: \n", tourBest)
    print("Best value: {:.1f}".format(valueBest))

    exit()

if __name__ == '__main__':
    main()

5、運行結果

程式的運行結果只供參考,顯然這并不是最優結果,

Tour verification successful!
Best tour: 
 [18  7 40  2 16 17 31 38 39 36 24 27 26 11 50  3  5  4 23 47 37 14 42  9
  8 32 10 51 13 12 25 46 28 29  1  6 41 20 30 21  0 48 35 34 33 43 45 15
 49 19 22 44]
Best value: 9544.0

著作權說明:
原創作品
Copyright 2021 YouCans, XUPT
Crated:2021-05-04

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/282865.html

標籤:Python

上一篇:自定義mybatis持久層框架

下一篇:模擬退火演算法Python編程(4)旅行商問題

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more