助力新能源車 意法半導體推新款車規碳化矽二極體

意法半導體推出新款車規碳化矽(SiC)二極體,以滿足電動汽車和插電式混合動力車(PHEVs,Plug-in Hybrids)等新能源汽車對車載充電器(OBCs,on-board battery chargers)在有限空間內處理大功率的苛刻要求。  

  新款二極體採用先進的技術可防止高電流突波燒毀裝置,其過電流保護是額定電流的2.5倍,因此設計人員可選用更小、更經濟實惠且可靠性和效能都不會受到影響的電流更小的二極體。此新碳化矽二極體通過車規產品測試,反向擊穿電壓提高到650V,能滿足設計人員和汽車廠商欲降低電壓補償係數的要求,以確保車載充電半導體元件的標準與瞬間峰值電壓之間有充足的安全邊際。   這次推出的650V二極體包括TO-220AC功率封裝的10A STPSC10H065DY和TO-220AC封裝的12A STPSC12H065DY。此外,TO-220AB封裝的STPSC20H065CTY和TO-247封裝的STPSC20H065CWY是內建2個10A二極體的雙二極體(dual-diode )產品,可最大幅度地提升空間利用度並減少車載充電器的重量。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

Python 簡明教程 — 19,Python 類與對象

微信公眾號:碼農充電站pro
個人主頁:https://codeshellme.github.io

那些能用計算機迅速解決的問題,就別用手做了。
—— Tom Duff

目錄

上一節 我們介紹了Python 面向對象的相關概念,我們已經知道類與對象是面向對象編程中非常重要的概念。

類就是一個模板,是抽象的。對象是由類創建出來的實例,是具體的。由同一個類創建出來的對象擁有相同的方法和屬性,但屬性的值可以是不同的。不同的對象是不同的實例,互不干擾。

1,類的定義

如下,是一個最簡單的類,實際上是一個空類,不能做任何事情:

class People:
    pass

在Python 中定義一個類,需要用到class 關鍵字,後邊是類名,然後是一個冒號:,然後下一行是類中的代碼,注意要有縮進。

2,創建對象

People 雖然是一個空類,但依然可以創建對象,創建一個對象的語法為:

對象名 = 類名(參數列表)

參數列表是跟__init__ 構造方法相匹配的,如果沒有編寫__init__ 方法,創建對象時,就不需要寫參數,如下:

>>> p = People()
>>> p
<__main__.People object at 0x7fd30e60be80>
>>> 
>>> p1 = People()
>>> p1
<__main__.People object at 0x7fd30e60be48>

p 和 p1 都是People類的對象。0x7fd30e60be80 是p 的地址,0x7fd30e60be48 是p1 的地址。可以看到不同的對象的地址是不同的,它們是兩不同的實例,互不干擾。

3,屬性

類中可以包含屬性(類中的變量),創建出來的對象就會擁有相應的屬性,每個對象的屬性的值可以不同。

創建好對象后,可以用如下方法給對象添加屬性:

>>> p = People()
>>> p.name = '小明' # 添加 name 屬性
>>> p.sex = '男'    # 添加 sex 屬性
>>> p.name         # 訪問對象的屬性
'小明'
>>> p.sex          # 訪問對象的屬性
'男'

雖然在技術上可以這樣做,但是一般情況下,我們並不這樣為對象添加屬性,這樣會破壞類的封裝性,使得代碼混亂,不利於維護。

當訪問一個不存在的屬性時,會出現異常:

>>> p.job         # 一個不存在的屬性
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'People' object has no attribute 'job'

我們一般會在__init__ 方法中為類添加屬性並賦值。

4,__init__ 方法

在Python 的類中,以雙下劃線__開頭和結尾的方法,被稱為魔法方法,每個魔法方法都有特定的含義。Python 為我們規定了一些魔法方法,讓我們自己實現這些方法。

__init__ 方法叫做構造方法,用來初始化對象。Python 解釋器會在生成對象時,自動執行構造方法,而無需用戶显示調用。

__init__ 方法不需要有返回值。

類中的所有實例方法 方法,都至少有一個參數,就是self。Python 中的self 相當於C++ 和Java 中的this 指針,都是代表當前對象。只是Python 中的self 需要显示寫在方法的第一個參數,而this 指針則不需要寫在方法參數中。

構造方法一般用於初始化對象的一些屬性,構造函數可以不寫,也可以只有一個self 參數。

當構造函數只有一個self 參數時,創建該類的對象時,不需要添加參數。當構造函數除了self 參數還有其它參數時,創建該類的對象時,則需要添加相匹配的參數。

比如,我們定義一個People 類,它有三個屬性,分別是name,sex,age:

class People:

    def __init__(self, name, sex, age):
        self.name = name
        self.sex = sex
        self.age = age
        print('執行了 __init__ 方法')

    def print_info(self):
        print('people:%s sex:%s age:%s' % (
            self.name, self.sex, self.age))

在這個People 類中除了有一個__init__ 方法外,還有一個print_info 方法,每個方法中的都有self 參數,並且是第一個參數,self 代表當前對象。

在創建該類的對象時,需要傳遞匹配的參數(self 參數不用傳遞):

>>> p = People('小明', '男', 18)
執行了 __init__ 方法
>>> p 
<People.People object at 0x7feb6276bda0>
>>> p.print_info()
people:小明 sex:男 age:18
>>>
>>> p1 = People('小美', '女', 18)
執行了 __init__ 方法
>>> p1
<People.People object at 0x7fd54352be48>
>>> p1.print_info()
people:小美 sex:女 age:18

可以看到,在創建p 和p1 對象時,字符串執行了 __init__ 方法 被打印了出來,而我們並沒有显示調用該方法,說明__init__ 方法被默認執行了。

對象p 和p1 是兩個不同的對象,擁有相同的屬性和方法,但是屬性值是不一樣的。兩個對象互不干擾,對象p 的地址為0x7feb6276bda0,p1 的地址是0x7fd54352be48。

執行代碼p.print_info(),是調用p 對象的print_info() 方法,因為,在定義該方法的時候,只有一個self 參數,所以在調用該方法的時候,不需要有參數。

5,私有屬性和方法

私有屬性

普通的屬性,就像上面的name,sex和age 屬性,都是公有屬性,在類的外部都可以被任意的訪問,就是可以用對象.屬性名的方式來訪問屬性,如下:

>>> p = People('小明', '男', 18)
執行了 __init__ 方法
>>> p.name  # 訪問屬性
'小明'
>>> p.name = '小麗'  # 修改屬性
>>> p.name  # 訪問屬性
'小麗'

這樣就破壞了數據的封裝性,這種訪問方式是不可控(會不受限制的被任意訪問)的,不利於代碼的維護,不符合面向對象的編程規範。

所以,通常我們會將類中的屬性,改為私有屬性,就是不能以對象.屬性名 這樣的方式訪問類屬性。

在Python 中,通過在屬性名的前邊添加雙下劃線__,來將公有屬性變為私有屬性,如下:

#! /usr/bin/env python3

class People:

    def __init__(self, name, sex, age):
        self.__name = name   # 兩個下劃線
        self.__sex = sex     # 兩個下劃線
        self._age = age      # 一個下劃線
        print('執行了 __init__ 方法')

    def print_info(self):
        print('people:%s sex:%s age:%s' % (
            self.__name, self.__sex, self._age))

這樣就無法通過對象.屬性名的方式來訪問屬性了,如下:

>>> p = People('小美', '女', 18)
執行了 __init__ 方法
>>> p.__name        # 出現異常
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'People' object has no attribute '__name'

但是,Python 中這種私有屬性的方式,並不是真正的私有屬性,Python 只是將__name 轉換為了_People__name,即是在__name 的前邊加上了_類名(_People),我們依然可以這樣訪問__name 屬性:

>>> p._People__name
'小美'

但我們並不提倡這種方式,這會讓代碼變得混亂難懂。

可以注意到,People 類中的_age 屬性是以單下劃線開頭的,這種以單下劃線開頭的屬性是可以在類的外部被訪問的:

>>> p._age
18

但是根據Python 規範,以單下劃線開頭的屬性,也被認為是私有屬性,也不應該在類的外部訪問(雖然在技術上是可以訪問的)。

注意:以雙下劃線__ 開頭且結尾的屬性__xxx__,是特殊屬性,是公有的,可在類的外部訪問

私有方法

私有方法與私有屬性類似,也可以在方法名的前邊加上雙下劃線__,來將某個方法變成私有的,一般不需要被外部訪問的方法,應該將其設置為私有方法。

6,set 和 get 方法

為了數據的封裝性,我們不應該直接在類的外部以對象.屬性名的方式訪問屬性,那麼如果我們需要訪問類的屬性該怎麼辦呢?

這時我們需要為每個私有屬性都提供兩個方法:

  • set 方法:用於設置屬性的值
  • get 方法:用於訪問屬性的值

為了減少代碼量,這裏只為__name 屬性設置了這兩個方法,代碼如下:

#! /usr/bin/env python3

class People:

    def __init__(self, name, sex, age):
        self.__name = name
        self.__sex = sex
        self._age = age
        print('執行了 __init__ 方法')

    def print_info(self):
        print('people:%s sex:%s age:%s' % (
            self.__name, self.__sex, self._age))

    # set 和 get 方法
    def set_name(self, name):
        self.__name = name

    def get_name(self):
        return self.__name

用戶可以這樣設置和訪問類的屬性:

>>> from People import People
>>> p = People('小美', '女', 18)
執行了 __init__ 方法
>>> p.get_name()         # 獲取 name 值
'小美'
>>> p.set_name('小麗')   # 設置新的值
>>> p.get_name()        # 再次獲取name 值
'小麗'

因為這種set 和 get 方法,是由類的開發者提供的,是被開發者控制的。

類的開發者會根據需要,來控制類的使用者如何使用該類,即哪些類的屬性和方法應該被使用者訪問,以及如何被使用者訪問。

如此,類的使用者就不能隨便的訪問類中的屬性,這就達到了封裝的目的。

(完。)

推薦閱讀:

Python 簡明教程 — 14,Python 數據結構進階

Python 簡明教程 — 15,Python 函數

Python 簡明教程 — 16,Python 高階函數

Python 簡明教程 — 17,Python 模塊與包

Python 簡明教程 — 18,Python 面向對象

歡迎關注作者公眾號,獲取更多技術乾貨。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

第五屆中國國際新能源汽車論壇2015

第五屆中國國際新能源汽車論壇2015強勢回歸,5月18、19日重聚上海

2014年5月18-19日∣中國•上海

新能源汽車“智”造時代

為了全面促進我國新能源汽車產業的快速發展,給全球新能源汽車產業的參與者提供一個溝通交流的平臺,在連續四屆精彩紛呈的新能源汽車論壇的基礎上,由中國產業海外發展和規劃協會、德國電動車協會、上海交大密西根學院主辦,希邁商務諮詢承辦,並得到了亞太電動車協會的傾情指導的“第五屆中國國際新能源汽車論壇2015”將於2015年5月18日至19日在上海隆重召開。屆時將有三百位行業領導出席本次論壇。

互聯網智慧時代強勢來襲,且正悄然改變著汽車這個傳統的行業。汽車互聯網時代的到來也促進著新能源汽車、汽車租賃等新形態汽車業務有效開展。汽車智慧化、新能源、輕量化成為汽車發展的新趨勢。

在過去的四屆新能源汽車論壇上,主辦方成功的邀請了包括國家發改委能源研究所、世界電動車協會、亞太電動車協會、世界氫能協會、世界分散式能源聯盟、中國工程院、美國工程師學會、國家863重大項目組、北京公共交通集團、上海交通大學、清華大學、國家電網、中國南方電網、普天新能源等在內的政府單位與研究機構,以及包括寶馬、賓士、奇瑞捷豹路虎、大眾、奇瑞、奧迪、比亞迪、上汽、北汽、東風、本田、豐田、現代、吉利、宇通客車等在內的知名整車商,共同參與和討論,對新能源汽車行業政策趨勢、技術路線、技術難點、基礎設施建設、商業模式等進行了積極的探討,取得了豐碩的成果,獲得了業內外人士的一致好評。

在嶄新的2015年,組委會為感謝業內外人士對系列論壇長期以來的關注和支援,將傾情奉上第五屆中國國際新能源汽車論壇2015,本次論壇將以新能源汽車“智”造時代為主題。現誠摯邀請您參加本次會議共創輝煌。屆時將邀請全球範圍內的整車製造商、電網公司、電力公司、電池廠商、零部件供應商、核心技術提供商和政府官員近三百位行業嘉賓一起,就新能源汽車產業面臨的機遇、挑戰和對策進行為期兩天的富有建設性和戰略性的討論。

往屆會議精彩回顧

第一屆中國國際新能源汽車論壇 第二屆中國國際新能源汽車論壇
第三屆中國國際新能源汽車論壇 第四屆中國國際新能源汽車論壇

部分往屆知名發言人:

會議亮點

  • 參會嘉賓:
    270+高度滿意的企業決策者,90+業內知名企業,20+國家和地區
  • 參會嘉賓分析:
    17%+來自各國政府部門及權威機構,25%+來自知名整車商
  • 演講嘉賓:
    30+世界新能源汽車行業知名發言嘉賓
  • 交流機會:
    16+小時的交流機會:圓桌討論、VIP午宴和開放式問答
  • 會議形式:
    2天高品質的乾貨分享,整車廠商親臨電池企業對接洽談會

會議結構

  上午 下午
大會第一天

“智”造時代政策預測、製造趨勢及試點城市推廣情況

工信部、科技部、交通部、發改委及地方政府的政策支持
汽車智慧化、新能源、輕量化發展趨勢
國內外整車商新能源新動向
試點城市推廣情況介紹及遇到問題解決措施

新能源汽車動力總成電氣化及商業模式

新能源汽車開發特點及供應配套要求
動力總成電氣化
無人駕駛及無線充電

大會第二天

電池及儲能的研發、製造趨勢

動力電池未來商業模式、技術路線探索
燃料電池及材料研發
動力電池最新技術工藝及典型生產設備選型

微型電動車市場准入

政策准入前景
市場格局變化
電池技術路線選擇

整車廠商-電池企業

對接洽談會

第五屆中國國際新能源汽車論壇2015特色

新能源汽車“智”造時代,第五屆中國國際新能源汽車論壇2015將會邀請政府主管機構、整車商及行業各個領域的領先者,對行業現有的發展訴求及發展願景規劃做出積極的探討。

想瞭解詳細內容,請登陸官方網站:
連絡人:Hill ZENG(曾先生)
電話:+86-21-6045 1760
傳真:+86-21-6047 5887
郵箱:

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

動態追蹤技術之SystemTap

SystemTap

從春哥(章亦春)那看到的關於SystemTap的介紹,讓我對動態追蹤這個功能頗為感興趣,覺得這是一個非常有用的功能。

SystemTap對內核及用戶態程序提供了動態追蹤功能,用戶可以自定探測事件來跟蹤程序的運行情況,如函數的調用路徑、CPU佔用和磁盤IO等一系列可以探測的情況。有了systemtap,可以在程序不修改代碼,甚至不用重啟就能分析出程序的運行情況。

配合火焰圖的可視化,對程序的性能分析極其有利。(放一個FlameGraph的官方圖片)

原理

SystemTap 基本思想是命名事件,併為它們提供處理程序。每當發生指定的事件時,內核都會將處理程序視為子例程運行,然後繼續運行。有一系列的事件,例如進入或退出函數,計時器到期或整個SystemTap會話的開始和停止。處理程序是一系列腳本語言語句,用於指定事件發生時要完成的工作。這項工作通常包含從事件上下文中提取數據,將其存儲到內部變量或打印結果。

SystemTap 的工作原理是將腳本翻譯成C語言,執行C編譯器創建一個內核模塊。當模塊被加載后,通過掛載到內核來激活所有的探測事件。然後,當事件發生再任何處理器上時,編譯后的處理程序就運行,最終,SystemTap會話停止,Hook取消,內核模塊被移除,整個過程由命令行程序stap驅動。

原理圖如下:

安裝

需要內核級別的支持,依賴了三個rpm下載鏈接,

  1. kernel-debuginfo-common
  2. kernel-debuginfo
  3. kernel-devel

這三個包的版本必須匹配當前內核的版本,比如我自己的內核版本是 3.10.0-327, 那麼以上三個包版本都必須保持一致。

安裝 SystemTap:

$ yum install systemtap

測試是否成功安裝:

$ stap -ve ‘probe begin{printf(“Hello, World\n”); exit();}’

正常的話會輸出 Hello, World,但是不出意外會出現版本不一致的情況:

ERROR: module version mismatch (#1 SMP Fri Nov 20 11:12:42 CST 2015 vs #1 SMP Thu Nov 19 22:10:57 UTC 2015), release 3.10.0-327.el7.x86_64

出現這個的情況是版本相同但是打包事件不相同的情況,修改這個時間和uname -a 中的時間保持一致。

$ rpm -ql kernel-devel | xargs grep UTS_VERSION 2>/dev/null

/usr/src/kernels/3.10.0-327.el7.x86_64/include/generated/compile.h:#define UTS_VERSION "#1 SMP Fri Nov 20 11:12:42 CST 2015"

再次運行那個hello測試,出現緩存的錯誤,刪除緩存文件

  1. /root/.systemtap/cache/34/stap_34443d4ad1fe1d37c0352b7b8c691aee_975.c
  2. /root/.systemtap/cache/34/stap_34443d4ad1fe1d37c0352b7b8c691aee_975.ko

追蹤

最簡單的探測類型就是跟蹤事件。Systemtap支持許多內置事件,所有的事件家族見 tapset

可以探測的的常用事件:

  • begin, systemtap 會話開始
  • end, systemtap 會話結束
  • kernel.function(“sys_xxx”), 系統調用xx的入口
  • kernel.function(“sys_xxx”).return, 系統調用xx的返回
  • timer.ms(300), 每300毫秒的定時器
  • timer.profile, 每個CPU上周期觸發的定時器
  • process(“a.out”).function(“foo*”), a.out 中函數名前綴為foo的函數信息
  • process(“a.out”).statement(“*@main.c:200”), a.out中文件main.c 200行處的狀態

常用的可打印值(具體見 tapset):

  • tid(), 當前線程id
  • pid(), 當前進程id
  • uid(), 當前用戶id
  • execname(), 當前進程名稱
  • cpu(), 當前cpu編號
  • gettimeofday_s(), 秒時間戳
  • get_cycles(), 硬件周期計數器快照
  • pp(), 探測點事件名稱
  • ppfunc(), 探測點觸發的函數名稱
  • $$var, 上下文中存在 $var,可以使用該變量
  • print_backtrace(), 打印內核棧
  • print_ubacktrace(), 打印用戶空間棧

SystemTap 腳本

stap 腳本簡單,語法類似C;

  • 註釋
# fuck
// fuck
/* fuck */
  • 函數
function foo() {
    // exit(); // 退出 systemtap 會話
}
  • 基本的 if/else/while/for 控制結構
function if_expr() {
    i = 0
    if (i == 1)
        printf("[if] i = %d\n", i);
    else
        printf("[else] i = %d\n", i);
}

function while_expr() {
    i = 0;
    while (i != 2)
        printf("[while] i = %d\n", i++);
}

function for_expr() {
    for (i = 0; i < 2; i++)
        printf("[for] i = %d\n", i);
}

  • 字符串比較,拼接,轉換
function str() {
    uid = uid();
    s_uid = sprint(uid);
    f_uid = "fuck" . s_uid
    printf("uid: %d-%s-%s\n", uid, s_uid, f_uid); // uid: 0-0-fuck0

    // exit();
}
  • 元組
global t; // 聲明元組
global tpl[400]; // 聲明一個400容量的元組

t["fuck"]++;  // t["fuck"] 初始值默認為0, ++ 變成 1
t["fuck"] = 4396; // 賦值為4396

tpl["fuck", pid()]++; // 兩個元素
tpl["shit", tid()]++;
  • 聚集統計
// 包含4個維度 @count @avg @min @max
global t;

t["fuck", tid()] <<< 1
t["fuck", pid()] <<< 1
t[execname(), tid()] <<< 1
t["fuck", 5487] <<< 2
t["fuck", 5487] <<< 3
t["fuck", 5487] <<< 1

具體結構如下:
t["fuck",5487] @count=3 @min=1 @max=3 @sum=6 @avg=2
t["fuck",26060] @count=2 @min=1 @max=1 @sum=2 @avg=1
t["stapio",26060] @count=1 @min=1 @max=1 @sum=1 @avg=1


// 遍歷(升序), 限制5次循環
foreach([key, value] in t+ limit 5)
    printf("%s: %d\n", key, value)

// 結果
stapio: 2571
fuck: 2571
fuck: 5487

應用

stap 常用命令

Usage: stap [options] FILE                    Run script in file.
   or: stap [options] -e SCRIPT               Run given script.
   or: stap [options] -l PROBE                List matching probes.
   or: stap [options] -L PROBE                List matching probes and local variables.
[options]
   -T TIME    terminate the script after TIME seconds

除了直接執行腳本文件外,另外一個比較有用的功能 -L -l 現象,列出可探測點及局部變量

  • 列出程序中的可探測點
// 截取部分~
[root@localhost stp]# stap -l 'process("/tmp/limlog/build/tests/LogTest").function("*")'
process("/tmp/limlog/build/tests/LogTest").function("write@/tmp/limlog/limlog/Log.cpp:107")
process("/tmp/limlog/build/tests/LogTest").function("~LimLog@/tmp/limlog/limlog/Log.cpp:213")
process("/tmp/limlog/build/tests/LogTest").function("~LogLine@/tmp/limlog/limlog/Log.cpp:341")
process("/tmp/limlog/build/tests/LogTest").function("~LogSink@/tmp/limlog/limlog/Log.cpp:59")
process("/tmp/limlog/build/tests/LogTest").function("~_Impl@/usr/include/c++/4.8.2/thread:107")
process("/tmp/limlog/build/tests/LogTest").function("~_Impl_base@/usr/include/c++/4.8.2/thread:97")
  • 列出程序中的可探測點及局部變量(前綴為$)
[root@localhost stp]# stap -L 'process("/tmp/limlog/build/tests/LogTest").function("*")'
process("/tmp/limlog/build/tests/LogTest").function("id@/usr/include/c++/4.8.2/thread:73") $this:class id* const
process("/tmp/limlog/build/tests/LogTest").function("incConsumable@/tmp/limlog/limlog/Log.cpp:313") $this:class LimLog* const $n:uint32_t
process("/tmp/limlog/build/tests/LogTest").function("incConsumablePos@/tmp/limlog/limlog/Log.cpp:135") $this:class BlockingBuffer* const $n:uint32_t
process("/tmp/limlog/build/tests/LogTest").function("incConsumablePos@/tmp/limlog/limlog/Log.cpp:460") $n:uint32_t
process("/tmp/limlog/build/tests/LogTest").function("insert@/usr/include/c++/4.8.2/bits/basic_string.h:1319") $__c:char $__n:size_type $__pos:size_type $this:class basic_string<char, std::char_traits<char>, std::allocator<char> >* const

有這個功能,我們就可以看到函數所在源文件中的位置及可以根據的局部變量。

systemtap 腳本

探針事件的關鍵字是 probe, 由 probe 引出需要跟蹤的事件

// 腳本執行后显示 === begin ===
probe begin {
    printf("=== begin ===\n");
}

根據上述的可跟蹤的事件

  • begin/end, 分別是systemtap會話的起始和結尾
[root@localhost stp]# cat foo.stp
#!/usr/bin/env stap

probe begin {
    printf("=== begin ===\n");
}

probe end {
    printf("=== end ===\n");
}

// 執行腳本
[root@localhost stp]# stap foo.stp -T 1
=== begin ===
=== end ===
  • kernel.function(“sys_xxx”), 系統調用
// 調用open系統調用
[root@localhost stp]# cat foo.stp
#!/usr/bin/env stap

probe kernel.function("sys_open").call {
    printf("%s call %s\n", execname(), ppfunc());
}

// open系統調用返回
probe kernel.function("sys_open").call {
    printf("%s call %s over\n", execname(), ppfunc());
}

[root@localhost stp]# stap foo.stp -T 1
sh call SyS_open
sh call SyS_open over
sh call SyS_open
sh call SyS_open over
sh call SyS_open
sh call SyS_open over
  • 定時器調用
[root@localhost stp]# cat foo.stp
#!/usr/bin/env stap

// 定時調用函數
probe timer.ms(500) {
    printf("now: %d\n", gettimeofday_s());
}

[root@localhost stp]# stap foo.stp -T 3
now: 1593141081
now: 1593141081
now: 1593141082
now: 1593141082
now: 1593141083
  • 定時cpu採樣
// 取自春哥的sample-bt示例代碼,定時調用棧取樣
global bts;

probe timer.profile {
    if (pid() == 5291)
        bts[backtrace(), ubacktrace()] <<< 1
}

probe timer.s(10) {
    foreach([k, u] in bts-) {
        print_stack(k);
        print_ustack(u);
        printf("\\t%d\\n", @count(bts[k, u]));
    }
    exit();
}

// 結果為16進制地址,截取一部分數據,這部分數據需要進一步加工統計
[root@localhost stp]# stap foo.stp
 0xffffffff810d6244 : 0xffffffff810d6244
 0xffffffff810475fa : 0xffffffff810475fa
\t1\n 0xffffffffa0139f0d : 0xffffffffa0139f0d [xfs]
  • 指定程序中的函數事件(需要運行程序)
// 打印程序 LogTest 所有執行的函數
[root@localhost stp]# cat foo.stp
probe process("/tmp/limlog/build/tests/LogTest").function("*") {
    printf("function: %s\n", ppfunc());
}

// 庫函數也加載進來了
[root@localhost stp]# stap foo.stp
function: offsetOfPos
function: __distance<char*>
function: operator+<long int, std::ratio<1l, 1000000000l>, long int, std::ratio<1l, 1000000l> >
function: operator<<
  • 指定程序指定文件中的可追蹤事件
//打印 LogTest 程序屬於 Log.cpp 中的函數信息
[root@localhost stp]# cat foo.stp
probe process("/tmp/limlog/build/tests/LogTest").statement("*@Log.cpp") {
    printf("function: %s\n", ppfunc());
}

// 過濾了庫函數,只留下Log.cpp中的函數執行
[root@localhost stp]# stap foo.stp
function: singleton
function: produce
function: produce
function: produce
function: produce
function: used
function: used
function: consumable
function: operator<<
function: ~LogLine
  • 上下文變量
// 獲取上下文變量
[root@localhost stp]# stap -L 'process("/tmp/limlog/build/tests/LogTest").statement("*@Log.cpp")'
process("/tmp/limlog/build/tests/LogTest").statement("LimLog@/tmp/limlog/limlog/Log.cpp:185") $this:class LimLog* const
process("/tmp/limlog/build/tests/LogTest").statement("LogLine@/tmp/limlog/limlog/Log.cpp:336") $this:class LogLine* const $level:enum LogLevel $loc:struct LogLoc const&
process("/tmp/limlog/build/tests/LogTest").statement("append@/tmp/limlog/limlog/Log.cpp:346") $this:class LogLine* const $data:char const* $n:size_t
process("/tmp/limlog/build/tests/LogTest").statement("consumable@/tmp/limlog/limlog/Log.cpp:140") $this:class BlockingBuffer const* const
process("/tmp/limlog/build/tests/LogTest").statement("consume@/tmp/limlog/limlog/Log.cpp:146") $this:class BlockingBuffer* const $to:char* $n:uint32_t

// 這裏我們追蹤 consume 函數中的兩個參數
[root@localhost stp]# cat foo.stp
probe process("/tmp/limlog/build/tests/LogTest").function("consume") {
    printf("func = %s, $var(n) = %d, $var(to) = %p\n", ppfunc(), $n, $to);
}

[root@localhost stp]# stap foo.stp
func = consume, $var(n) = 406, $var(to) = 0x7f902a94d010
func = consume, $var(n) = 203, $var(to) = 0x7f902a94d1a6
func = consume, $var(n) = 790, $var(to) = 0x7f902a94d010
func = consume, $var(n) = 3319, $var(to) = 0x7f902a94d326
func = consume, $var(n) = 4235, $var(to) = 0x7f902a94d010
func = consume, $var(n) = 4235, $var(to) = 0x7f902a94d010
func = consume, $var(n) = 2326, $var(to) = 0x7f902a94d010
func = consume, $var(n) = 8470, $var(to) = 0x7f902a94d010

示例分析

在探測點被執行時打印這種簡單的用法外,還能進一步做一些其它的事情。

  1. 統計某一時間段內運行次數top k的函數
[root@localhost stp]# cat foo.stp
#!/usr/bin/env stap

global top_funcs;

probe begin {
    printf("=== begin ===\n");
}

probe process("/tmp/limlog/build/tests/LogTest").statement("*@Log.cpp") {
    top_funcs[ppfunc()]++;
}

probe end {
    foreach(func in top_funcs- limit 10)
        printf("%s: %d\n", func, top_funcs[func]);

    printf("=== end ===\n");
}

// LogTest 在3秒內執行最多的10個函數及次數
[root@localhost stp]# stap foo.stp -T 3
=== begin ===
produce: 1152
append: 720
singleton: 646
operator<<: 608
produceLog: 576
used: 576
consumable: 359
incConsumablePos: 64
getLogLevel: 32
LogLine: 32
=== end ===
  1. 統計系統調用某一時間段內被調用最頻繁的k個程序及次數
[root@localhost stp]# cat foo.stp
#!/usr/bin/env stap

global top_exec;

probe begin {
    printf("=== begin ===\n");
}

probe kernel.function("sys_write") {
    top_exec[execname()]++;
}

probe end {
    foreach(exec in top_exec- limit 10)
        printf("%s: %d\n", exec, top_exec[exec]);

    printf("=== end ===\n");
}

[root@localhost stp]# stap foo.stp -T 3
=== begin ===
qemu-kvm: 2948
sync: 2174
virsh: 673
libvirtd: 305
route: 28
grep: 21
ps: 21
ssh: 16
python: 16
nginx: 16
=== end ===
  1. 打印函數調用層次(用戶程序)

調用層次需要藉助 thread_indent 來打印合適的縮進,另外需要設置函數入口(call)和返回(return)的探測點。函數命名不足以使用function通配符來匹配,而 statement 雖然可以匹配我們想要的結果(過濾第三方函數),但是不支持 call 和return。雖然有一個比較傻但是的確可行的方式是,在腳本裏面手動寫入這些函數。

// 先取出需要關注的函數名稱
stap -L 'process("/tmp/limlog/build/tests/LogTest").statement("*@*.cpp")' |  sed 's/.*statement(\"\(.*\)@.*\".*/\1/g'  | sort -u

腳本如下:

#!/usr/bin/env stap

global f;

function init() {
    f["append"] = 1;
    f["benchmark"] = 1;
    f["consumable"] = 1;
    f["consume"] = 1;
    f["date"] = 1;
    f["datetime"] = 1;
    f["formatTimestamp"] = 1;
    f["getLogLevel"] = 1;
    f["gettid"] = 1;
    f["i16toa"] = 1;
    f["i2a"] = 1;
    f["i32toa"] = 1;
    f["i64toa"] = 1;
    f["incConsumable"] = 1;
    f["incConsumablePos"] = 1;
    f["LimLog"] = 1;
    f["~LimLog"] = 1;
    f["listStatistic"] = 1;
    f["log_10_diff_element_len"] = 1;
    f["log_10_diff_element_str"] = 1;
    f["log_10_diff_element_x1"] = 1;
    f["log_16_same_element_x6"] = 1;
    f["log_1_same_element_x6"] = 1;
    f["log_4_same_element_x6"] = 1;
    f["LogLine"] = 1;
    f["~LogLine"] = 1;
    f["LogSink"] = 1;
    f["~LogSink"] = 1;
    f["main"] = 1;
    f["now"] = 1;
    f["operator<<"] = 1;
    f["produce"] = 1;
    f["produceLog"] = 1;
    f["rollFile"] = 1;
    f["setLogFile"] = 1;
    f["setLogLevel"] = 1;
    f["setRollSize"] = 1;
    f["singleton"] = 1;
    f["sink"] = 1;
    f["sinkThreadFunc"] = 1;
    f["stringifyLogLevel"] = 1;
    f["test_blocking_buffer"] = 1;
    f["test_itoa"] = 1;
    f["test_timestamp"] = 1;
    f["time"] = 1;
    f["u16toa"] = 1;
    f["u2a"] = 1;
    f["u32toa"] = 1;
    f["u64toa"] = 1;
    f["used"] = 1;
    f["write"] = 1;
}

probe begin {
    printf("=== begin ===\n");
    init();
}

probe process("/tmp/limlog/build/tests/LogTest").function("*").call {
    fn = ppfunc()
    if (f[fn] == 1)
        printf("%s -> %s\n", thread_indent(4), ppfunc());
}

probe process("/tmp/limlog/build/tests/LogTest").function("*").return {
    fn = ppfunc()
    if (f[fn] == 1)
        printf("%s <- %s\n", thread_indent(-4), ppfunc());
}

probe end {
    printf("=== end ===\n");
}

函數的部分調用樹如下所示:

=== begin ===
     0 LogTest(25381):    -> main
   101 LogTest(25381):        -> setLogFile
   111 LogTest(25381):            -> singleton
   121 LogTest(25381):                -> LimLog
   129 LogTest(25381):                    -> LogSink
   270 LogTest(25381):                        -> now
   282 LogTest(25381):                        <- now
   288 LogTest(25381):                        -> date
   296 LogTest(25381):                            -> datetime
   453 LogTest(25381):                            <- datetime
   472 LogTest(25381):                        <- date
   478 LogTest(25381):                    <- LogSink
   576 LogTest(25381):                <- LimLog
   586 LogTest(25381):            <- singleton
   603 LogTest(25381):            -> rollFile
     0 LogTest(25382):    -> sinkThreadFunc
   650 LogTest(25381):            <- rollFile
   ···

樹狀調用可以很清楚的展示出來程序的執行邏輯,甚至可以看到多線程的處理:sinkThreadFunc 和 rollFile 之間的跨度。

  1. 開源庫對systemtap的集成
    像libguestfs 和libvirt都有支持,恰好工作環境這兩個庫也經常有一些難以排查的問題出現

觀察 libguestfs 的啟動過程及耗時,libvirt 支持function 和mark(有5個,大致邏輯上的埋點)

#! /usr/bin/env stap

global start_time, prev;

function display_time () {
  now = gettimeofday_us ()
  delta = (prev > 0 ) ? now - prev : 0
  printf ("%18d (+%10d): ", now - start_time, delta)
  prev = now
}

probe begin {
  start_time = gettimeofday_us ()
  prev = 0
  printf ("%-18s (+%10s): %s\n", "# time_usec",
          "delta usec", "event")
}

probe process("/usr/lib*/libguestfs.so.0*").function("*")
{
  display_time()
  printf ("%s\n", ppfunc())
}

打印的結果,如果使用 libguestfs-test-tools 執行失敗,可以根據產生的函數進入源代碼中進行排查,縮小排查的範圍

[root@localhost stp]# stap libguestfs.stp
# time_usec        (+delta usec): event
           1880754 (+         0): _init
           1880765 (+        11): frame_dummy
           1880768 (+         3): register_tm_clones
           1880772 (+         4): compile_regexp_re_hostname_port
           1880804 (+        32): init_libguestfs
           1881074 (+       270): guestfs_int_init_direct_backend
           1881079 (+         5): guestfs_int_register_backend
           1881083 (+         4): guestfs_int_init_libvirt_backend
           1881085 (+         2): guestfs_int_register_backend
           1881088 (+         3): guestfs_int_init_uml_backend
           ···

而 libvirt 中 example/ 目錄中就有包含一些關於 systemtap 的腳本文件。

火焰圖的生成

在函數調用樹之上更進一步,加入樣本的頻率得到svg格式的火焰圖,用戶可以點擊不同的圖層來查看更詳細的信息。

火焰圖工具為brendangregg開發的開源項目FlameGraph,配合春哥寫的systemtap工具,用一段簡單的shell腳本如下,可以得到一張svg的圖片

#!/bin/bash

# 火焰圖工具的路徑
FlameGraph_PATH="/FlameGraph"

# 春哥的棧收集工具
OR_Stap_PATH="/home/zxh/dev/openresty-systemtap-toolkit"

if [ $# != 1 ] ; then
        echo "Usage: sh $0 PID"
        echo "  e.g.: sh $0 4396"
        exit 1;
fi

${OR_Stap_PATH}/sample-bt -p $1 -t 180 -u > /tmp/bt-sample
${FlameGraph_PATH}/stackcollapse-stap.pl /tmp/bt-sample > /tmp/bt_stap.out
${FlameGraph_PATH}/flamegraph.pl /tmp/bt_stap.out > backtrace.svg

rm -rf /tmp/bt-sample
rm -rf /tmp/bt_stap.out

CPU定時採樣,根據採樣生成的 backtrace.svg 如下,橫軸為CPU佔用的時間比例,縱軸為函數調用的深度:

這是一個日誌庫的benchmark(博客園不支持上傳svg格式),而主要耗時也在阻塞隊列的入隊上,符合預期。

rpm 鏈接

  1. https://mirrors.ocf.berkeley.edu/centos-debuginfo/7/x86_64/kernel-debuginfo-common-x86_64-3.10.0-327.el7.x86_64.rpm
  2. https://mirrors.ocf.berkeley.edu/centos-debuginfo/7/x86_64/kernel-debuginfo-3.10.0-327.el7.x86_64.rpm
  3. ftp://ftp.pbone.net/mirror/ftp.scientificlinux.org/linux/scientific/7.0/x86_64/updates/security/kernel-devel-3.10.0-327.el7.x86_64.rpm

參考

  1. systemtap 原理圖, https://www.ibm.com/developerworks/cn/linux/l-cn-systemtap3/index.html?ca=drs-
  2. systemtap 官方教程(推薦),https://sourceware.org/systemtap/tutorial/1_Introduction.html
  3. systemtap 內置函數庫 tapset,https://sourceware.org/systemtap/tapsets/index.html
  4. 春哥的一系列stap工具,https://github.com/openresty/openresty-systemtap-toolkit
  5. 火焰圖工具,https://github.com/brendangregg/FlameGraph

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

福特攜兩款新智慧電動車 亮相MWC2015

在巴賽隆納舉辦的2015世界移動通信大會上,福特汽車公司宣佈了「智慧騎行」的試驗專案,首推智慧電動車,這也是一項通過在車載連接技術、移動出行、自動駕駛汽車、消費者體驗以及大資料方面的創新,説明改變世界出行方式的計畫。   首推的倆款智能電動車為MoDe:Pro與MoDe:Me。MoDe:Pro由福特團隊打造完成,用於商業用途。MoDe:Me在自行車製造商Dahon的協助下打造完成,便於摺疊存放。    

(圖片來源:騰訊)

    這兩款電動自行車都配備了200瓦的馬達和9安培小時的電池,提供電動踏板輔助,最高時速可達2萬5千公尺。原型電動自行車的技術可實現行車預警系統,在汽車超車時,透過車把手的震動向騎士們發出警告,還可通過開啟把手上的警示燈,提醒汽車司機注意電動自行車的存在。   MoDe:Me和MoDe:Pro可與一款名為MoDe:Link的原型應用程式配合使用,此款應用程式可與iPhone 6手機相容。通過應用程式中的即時資訊,電動自行車可實現導航、線路選擇、速度和舒適度調整等功能。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

【秒懂Java】【第1章_初識Java】01_編程語言

各位小夥伴們好哇!從今日起,我將開始更新《秒懂Java》系列文章,從0開始講解Java的方方面面,完全零基礎也可以看懂。後面也將推出配套的視頻版,歡迎大家保持關注!

  • 我會儘力辦到:在保證通俗易懂的同時,不丟失知識的嚴謹性和完整性
  • 不管你是否有編程經驗,只要你感興趣、細心閱讀,就能學會
  • 本文是《秒懂Java》系列的第1篇文章,主要認識幾個基礎概念

什麼是計算機?

一說到計算機(Computer),大家首先想到的可能是電腦(台式電腦、手提電腦)。

實際上,我們日常生活中使用的平板電腦、智能手機也屬於計算機,它們的功能也跟電腦一樣非常強大。

還有KTV的點歌機、超市的自助收銀機等很多設備也都屬於計算機。

毋庸置疑,計算機是人類歷史上最偉大的發明之一,極大地提高了社會的生產力,目前它已經延伸到了生活、學習、工作等各個領域,無處不在。如今,我們會在計算機上安裝各種各樣的軟件(應用、遊戲),學習工作、衣食住行、吃喝玩樂一網打盡,極大地改變了我們的生活、學習、工作方式。

什麼是編程語言?

語言,是雙方進行溝通交流的主要表達方式。

  • 如果我要跟咱們中國人進行交流,應該用漢語
  • 如果我要跟美國人進行交流,應該用英語
  • 如果我要跟韓國人進行交流,應該用韓語

那如果我要跟計算機進行交流呢?那就應該用計算機編程語言。

  • 是的,計算機編程語言,就是用來跟計算機進行溝通交流的語言
  • 一般把計算機編程語言簡稱為:編程語言(Programming Language)

注意:我們要學習的很多IT技術都源自西方國家(比如美國),因此,很多技術名詞,都是從英文翻譯過來的。為了保證描述的嚴謹性,當首次提及某個技術名詞時,我都會在它旁邊標註原本的英文單詞。

與計算機進行交流

那如何利用編程語言與計算機進行交流呢?

編程語言,顧名思義,就是可以用來“編寫程序”的語言。

  • 首先,利用編程語言編寫一段程序。例如上圖所示的程序,只包含了3行代碼,這裏簡單說一下代碼的大概意思(了解一下即可,不用去深究)
    • 第1行:創建了一個播放器
    • 第2行:設置播放器的音量為100
    • 第3行:開始播放某個mp4視頻
  • 最後,將程序運行到計算機上,計算機就會開始識別執行程序中的每一句代碼,完成相應的功能,最後成功播放視頻

我們平時使用的各種軟件(應用、遊戲)都是通過編程語言開發出來的,它們都由一大堆的代碼組成。當打開軟件時(將軟件運行到計算機上時),計算機就會開始識別執行軟件中包含的代碼,完成相應的功能。

所以,編程語言有一個非常重要的作用,那就是:開發軟件!

主流編程語言

世界上有上百種各式各樣的編程語言,目前比較常見的主流編程語言有

  • Java、C、C++、C#、PHP、Python、Go
  • JavaScript(簡稱JS)、TypeScript(簡稱TS)
  • Objective-C(簡稱OC)、Swift、Kotlin
  • Scala、Assembly Language(彙編語言) 等

每一門編程語言的作用都不太一樣,比如

  • Java、Kotlin:可以用來開發Android系統中的軟件
  • Objective-C、Swift:可以用來開發iOS、Mac系統中的軟件
  • C、C++、C#:可以用來開發Windows系統中的軟件

當然,上面列舉的僅僅是它們的部分功能,並沒有說完整。比如:

  • C、C++也可以開發Android、iOS、Mac、Linux等系統中的軟件
  • Java也可以開發Mac、Linux、Windows等系統中的軟件

其他的就不一一說明了,了解一下即可

語法

每當我們要學習一門新的語言時,都要先學習它的語法。

  • 比如,學習英語就要先學習它的語法,英語的語法規定了:英文句子可以怎麼寫、不可以怎麼寫
  • 同樣的,每一門編程語言都有自己的語法,編程語言的語法規定了:你的代碼可以怎麼寫、不可以怎麼寫
    • 比如,Java語言的語法規定:每一句代碼後面都必須以分號(;)結束
    • 代碼一旦出現了語法錯誤,就無法成功運行到計算機上

不同編程語言的語法是不一樣的,每一門編程語言都有自己特有的語法。比如,想讓計算機播放一個視頻,不同編程語言可能會有不同的寫法。

不難看出它們之間的一些差異(了解一下即可,不用去深究)

  • Java、OC都以分號(;)結束,而Python不需要以分號(;)結束
  • Java、Python中都使用了點(.)、小括號(()),而OC中使用了中括號([])、冒號(:)、@符號
  • Java、OC中都使用雙引號(“”),而Python中可以使用單引號(”)

雖然它們的寫法不一樣,但是都完成了一樣的功能:讓計算機播放一個視頻。如果想利用編程語言開發出強大優秀的軟件、控制計算機做更多的事情,首先要踏踏實實學好編程語言的語法。如果你連語法都不懂,怎麼可能寫出正確的代碼呢?

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

特斯拉 Model S 自駕模式「autopilot」將於 3 個月內推出

特斯拉執行長 Elon Musk 透露未來將透過 7.0 版本升級,為 Model S 車款帶來全新自動駕駛(self-driving)模式「autopilot」,預計在 3 至 4 個月內釋出軟體更新。  
  在新聞發佈會上,Elon Musk 公佈下一波 6.2 版本升級,新增里程保證(Range Assurance)和旅程規劃(Trip Planner)兩大關鍵功能。里程保證在於行駛過程中,系統會背景監控電力狀況並即時告知車主;旅程規劃則是在車主設定目的地之後,系統自動判斷行駛過程中需要充電的地點,規劃出最佳路線,這都是為了減輕部分車主的「里程焦慮」(Range Anxiety)問題。除了上述新功能之外,Elon Musk 還介紹了一些和主動安全相關的輔助功能,例如自動緊急剎車、盲區警告以及側面碰撞警告等。   不過,新聞發佈會上最令人興奮的是,Elon Musk 透露未來將透過 7.0 版本升級,為 Model S 車款帶來全新自駕模式「autopilot」。   Elon Musk 指出,擁有 Model S 車款的車主基本上可透過 autopilot 模式,做到往返舊金山和西雅圖之間無須任何操控動作。不過他也謹慎地表示,這還不算是真正的自動駕駛技術,只不過是強大的自動轉向系統。autopilot 不足之處,在於它僅適用於行駛在高速公路上,以該公司目前的技​​術,可能還無法安全地駛入郊區,畢竟街上難免還是會有孩童或路人竄出。欲使用 autopilot 模式,車主還需要選配特斯拉所提供價值 4,250 美元的 optional tech 方案。     本文全文授權轉載自《科技新報》─〈〉

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

你已經是個成熟的程序員了,該學會用程序幫自己省錢了————狄克斯特拉算法

  說起回家,路途漫漫,行李滿滿,尤其我等村裡交通不發達的地方,可能連直達的票都沒有,雖說條條大陸通羅馬,但畢竟還是想找個換乘最少的路線,畢竟誰不想回家更輕鬆點呢(*^_^*),下面就是我回家的所有路線。

 

  思路很簡單,先找起點看是否能到,不能到的話,看起點能到的點的下一步是否能到

 

  話不多說,擼代碼:

public static void main(String[] args) {
    HashMap<String,List<String>> data = new HashMap<String, List<String>>();
    List<String> list1 = new ArrayList<String>();
    data.put("起點",list1);
    list1.add("A");
    list1.add("B");
    List<String> list2 = new ArrayList<String>();
    data.put("A",list2);
    list2.add("終點");
    List<String> list3 = new ArrayList<String>();
    data.put("B",list3);
    list3.add("A");
    list3.add("終點");
    query(data,"終點","起點");
}

public static void query(Map<String,List<String>> data, String queryValue, String start){
    if(data==null || queryValue ==null){
        return;
    }
    Queue<String> queue = new LinkedList<String>();
    Map quaryLog  = new HashMap();
    Map<String,List<String>> routes = new HashMap<String, List<String>>();
    queue.offer(start);
    quaryLog.put(start,"");
    String parent = null;
    while (!queue.isEmpty()){
        parent = queue.poll();
        List<String> values = data.get(parent);
        for(String value:values){
            List<String> r = new ArrayList<String>();
            if(routes.containsKey(parent)){
                r.addAll(routes.get(parent));
            }
            r.add(parent);
            routes.put(value,r);
            if(queryValue.equals(value)){
                routes.get(value).add(value);
                System.out.println(routes.get(value));
                return;
            }
            if(!quaryLog.containsKey(value)){
                queue.offer(value);
                quaryLog.put(value,"");
            }
        }
    }
    return ;
}

  

  run 一把,結果出來了

  [起點, A, 終點]

  終於,結果出來了,先到A地,再從A到終點,其實這就是廣度優先搜索,so easy興沖衝去買票,發現錢不夠,哎,沒有考慮票價啊!!!我的票價是這樣的:

 

  按照現在的規劃需要700元,可是我只有650元,不夠啊,沒辦法,修改算法把,這次需要把價錢考慮進去,我需要最便宜的路線

  思路也類似,先從起點開始走,分別計算最便宜的路線

 

  終點暫時到不了,我們把到終點的距離記作無窮,接着我們從B點開始往下找,計算最便宜的價錢如下:

 

  然後再計算A點走的話,最便宜的路線,比從B點走便宜的話我們就更新,不便宜的話代表原來的價錢已經是最便宜的了

 

  找到了,最便宜的路線是600,但是程序要如何做呢,畢竟我以後不僅要回家,還要去旅遊,還要去丈母娘家,我要每次都最便宜!!!,擼碼如下:


public static void main(String[] args) {
HashMap<String,HashMap<String,Integer>> data = new HashMap<String, HashMap<String, Integer>>();
HashMap<String,Integer> map1 = new HashMap<String, Integer>();
data.put("起點",map1);
map1.put("A",600);
map1.put("B",200);
HashMap<String,Integer> map2 = new HashMap<String, Integer>();
data.put("A",map2);
map2.put("終點",100);
HashMap<String,Integer> map3 = new HashMap<String, Integer>();
data.put("B",map3);
map3.put("終點",500);
map3.put("A",300);
queryMinPrice(data,"起點","終點");
}

public static void queryMinPrice(HashMap<String,HashMap<String,Integer>> data,String start,String end){
HashMap<String,Integer> costs = new HashMap<String, Integer>();
HashMap<String,List<String>> route = new HashMap<String, List<String>>();
for(Map.Entry<String,Integer> entry: data.get(start).entrySet()){
costs.put(entry.getKey(),entry.getValue());
List<String> list = new ArrayList<String>();
list.add(entry.getKey());
route.put(entry.getKey(),list);
}
costs.put(end,Integer.MAX_VALUE);
HashMap<String,String> queryLog = new HashMap<String, String>();
String key = findMinPriceKey(costs,queryLog);
while (key != null){
queryLog.put(key,"");
if(data.get(key) == null){
break;
}
for(Map.Entry<String,Integer> entry:data.get(key).entrySet()){
if(costs.containsKey(entry.getKey())){
if(entry.getValue()+costs.get(key)<costs.get(entry.getKey())){
costs.put(entry.getKey(),entry.getValue()+costs.get(key));
List<String> list = new ArrayList<String>();
list.addAll(route.get(key));
list.add(entry.getKey());
route.put(entry.getKey(),list);
}
}else {
costs.put(entry.getKey(),entry.getValue()+costs.get(key));
List<String> list = new ArrayList<String>();
list.addAll(route.get(key));
route.put(entry.getKey(),list);
}
}
key = findMinPriceKey(costs,queryLog);
}
System.out.println("最小花費:"+costs.get(end));
System.out.println("最小花費路徑:"+route.get(end));
}
private static String findMinPriceKey(HashMap<String,Integer> data,HashMap<String,String> queryLog){
String key = null;
for(Map.Entry<String,Integer> entry : data.entrySet()){
if(!queryLog.containsKey(entry.getKey()) && key == null ){
key = entry.getKey();
}
if(!queryLog.containsKey(entry.getKey()) && entry.getValue()<data.get(key)){
key = entry.getKey();
}
}
return key;
}

  運行結果:

  最小花費:600

  最小花費路徑:[B, A, 終點]

  結果出來了,先買到B的票,然後在到A,再回家,只要600塊,還能省50塊,完美!!這就是大名鼎鼎的狄克斯特拉算法。

      PS:這不是標準的狄克斯特拉算法,只是本例中的數據少,數據多的話遍歷成本是很高的,標準的狄克斯特拉算法利用的是貪婪算法思想,求局部最優解,要在可以到達終點時就返回(此註釋基於評論,本來自己寫着玩兒的,沒想會有人認真來看,避免產生誤解,誤導他人,水平有限深感慚愧)

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

用心維護、挺過雨災、發電還增兩成 日本太陽光電廠優良案例

文:宋瑞文(加州能源特約撰述)

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

歐洲電動車銷售佳 較 2013 年成長近 6 成

歐洲環保署(European Environment Agency)最新統計顯示,歐洲掀起電動車革命,其中又以法國最為熱銷,佔了歐盟電動車市場的四分之一以上。   資料顯示,2014 年已登記的電動車大約有 3 萬 8 千輛,相較於 2013 年上升了 57%。其中,法國的電動車數量居全歐洲之冠,目前國內共有超過 1 萬 700 輛,德國以 8,500 輛位居第二,英國則以 6,700 輛暫居第三。   這項報告不但證明歐洲現今電動車市場方興未艾,也發現 2014 年售出的新車所排放的二氧化碳量,平均較 2013 年售出的車款少了 2.6%。  

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案