澳洲發現無齒恐龍 與迅猛龍是近親

摘錄自2020年5月19日自由時報報導

澳洲發現新種無齒恐龍,跟暴龍、迅猛龍是近親!澳洲古生物學家日前發布了新品種恐龍,此種新恐龍雖然與暴龍和迅猛龍同屬於一個亞目,但卻有著長脖子、沒有牙齒、飲食習性也不相同等特點。學界尚未給予正式名稱,研究人員暫時將其稱作「伊拉夫羅龍(Era the Elaphrosaur)」。

根據《BBC》報導,伊拉夫羅龍在維多利亞州奧特韋角出土,作為新品種的獸腳亞目,這種新恐龍具有許多古代捕食者的熟悉體型,牠採用雙足站立,加上兩隻些微笨拙退化的前爪(小胳膊),甚至可能披覆著一層羽毛,但從鼻子到尾巴的身長只有約2公尺左右。

伊拉夫羅龍與其他獸腳亞目的最大區別是,其脖子較大多數獸腳類要長得多,且似乎只有小時候才有牙齒。隨著年齡的增長,伊拉夫羅龍的牙齒會逐漸退化,並留下角質的喙。該物種可能小時候吃肉、長大後又轉向吃素。不過研究人員暫時還無法確定,因為目前暫缺頭骨部分的化石(只有頸骨部分)。

【其他文章推薦】

※飲水機皆有含淨水功能嗎?

※貨梯使用安全與保養

※十大封口機人氣排行榜-烘焙必備幫手!

※高價位跟低價位的示波器又有何差異?

※POGOPIN基本常識有哪些? 

※空壓機這裡買最划算!

停止依賴中國稀土 美國防部提案擴大投資上限

摘錄自2020年5月19日自由時報報導

美國國防部已對國會提出一項提案,擴大對稀土的投資上限,以停止對中國的依賴程度,這些稀土可以用來製造飛彈和彈藥、極音速武器,及相關電子產品。如果美國可以重新生產稀土,中國打「稀土牌」的威脅程度將大幅降低。

根據《國防新聞》報導,美國國防部希望提高《國防生產法》的支出上限,在開採稀土上提升至最高17.5億美元(約新台幣523億),在微電子晶片上增至3.5億美元(約新台幣104億),當涉及到極音速武器時,將會沒有上限。據悉,此提案已於本月初提出,已納入國會正在起草的年度國防政策法案。

美國防部副部長洛德(Ellen Lord)去年8月曾表示,國防部正與澳洲進行談判,要求其為美軍提供稀土。澳洲Lynas公司擁有稀土礦,同時在馬來西亞也有精煉廠,可能是此計劃的核心。

【其他文章推薦】

※客製專屬滑鼠墊、可愛造型L夾、L型資料夾、透明證件套、手提袋,專業印刷設計廠商!  

※示波器鮮為人知的使用技巧?

※掌握產品行銷策略,帶你認識商品包裝設計基本要素

※買不起高檔茶葉,精緻包裝茶葉罐,也能撐場面!

※貨梯使用安全與保養

※高效率洗滌塔活性碳設備有哪些?

※專業模具開發-五金製品代工製作廠

新冠病毒是就業殺手!美太陽能產業五年的就業成長歸零

摘錄自2020年5月19日自由時報報導

路透報導,新冠病毒的大流行,導致美國太陽能產業五年來的就業成長歸零。

產業領袖周一(18日)表示,在疫情期間,美國砍掉了6萬5000多個太陽能產業工作,這個以遏止氣候變遷為目標領域五年來的就業成長倒轉回原點。美國貿易公會組織「太陽能產業協會」表示,因商店關門和居家避疫令而無法安裝太陽能設施,導致工作急劇流失。

美國整個經濟體4月流失逾2050萬個工作,是自經濟大蕭條以來受薪工作最急劇的下滑。這波裁員後預期人力剩下18萬8000多名員工,類似此產業在2014年的規模,與之前預測的30萬2000名員工相距甚遠。

【其他文章推薦】

※石墨與鑽石區別在哪?

※專業客製化禮物、贈品設計,辦公用品常見【L夾】搖身一變大受好評!!

※高價位跟低價位的示波器又有何差異?

※無塵擦拭布各大品牌廠商販售比價網!

※想知道CNC 自動車床與CNC車床有何區別??

※特殊螺絲工廠批發-精密零件專售

※餐飲器具首選, 多種功能側火烤爐設備,小本經營最佳生財器具

根據《BBC》報導,伊拉夫羅龍在維多利亞州奧特韋角出土

根據《BBC》報導,伊拉夫羅龍在維多利亞州奧特韋角出土,作為新品種的獸腳亞目,這種新恐龍具有許多古代捕食者的熟悉體型,牠採用雙足站立,加上兩隻些微笨拙退化的前爪(小胳膊),甚至可能披覆著一層羽毛,但從鼻子到尾巴的身長只有約2公尺左右。

伊拉夫羅龍與其他獸腳亞目的最大區別是,其脖子較大多數獸腳類要長得多,且似乎只有小時候才有牙齒。隨著年齡的增長,伊拉夫羅龍的牙齒會逐漸退化,並留下角質的喙。該物種可能小時候吃肉、長大後又轉向吃素。不過研究人員暫時還無法確定,因為目前暫缺頭骨部分的化石(只有頸骨部分)。

【其他文章推薦】

※空壓機這裡買最划算!

※新北市轉軸代工廠商詢價平台?

※高效率洗滌塔活性碳設備有哪些?

※新北市探針業者,哪家可以精準車製?

※必買推薦!上下火烤爐全台最便宜都在這!

※臭氧機的滅菌效果如何?

不敢搭巴士地鐵 美國自行車大缺貨

摘錄自2020年5月19日自由時報報導

紐約時報報導,疫情改變美國大城市居民的生活,宅在家的日子休閒娛樂變少,加上不敢搭巴士或地鐵,自行車成為最佳的運動和代步工具,因此造成自行車大缺貨。

根據市場研究公司N.P.D. Group,3月份的全美自行車和配件,以及修車服務的營業額比去年同期增加了近一倍。同月的通勤和健身自行車銷售額成長66%,休閒用自行車成長121%,兒童自行車增加59%,電動自行車也成長85%。

到了4月底,許多商店和批發商的低價自行車都賣光了。現在美國的自行車嚴重缺貨,受疫情中斷的全球供應鏈加緊趕工,以供應大量增加的需求。

紐約市4月宣布將暫時開放100英里路段給行人和自行車騎士,官員說,這可能變成永久政策。奧克蘭市計畫在疫情期間關閉10%街道,禁止汽車通行;西雅圖也將永久封閉20英里路段。

【其他文章推薦】

※自動旋轉烤玉米機比價站,幫你找好康~

※業界餐飲設備NO1.嘉冠烤爐

※高價位跟低價位的示波器又有何差異?

※臭氧機的滅菌效果如何?

※專業客製化禮物、贈品設計,辦公用品常見【L夾】搖身一變大受好評!!

※多功能滑鼠墊、多用途卡套、全客製贈品節慶促銷活動開跑中  

《韓聯社》報導,該化工廠的催化劑疑似在過高的壓力下爆炸而引發大火

《韓聯社》報導,該化工廠的催化劑疑似在過高的壓力下爆炸而引發大火,火勢已在下午3時30分(台灣時間下午2時30分)被撲滅,據消防部門表示,沒有有害化學物質外洩。目前該設施已關閉,警方和消防部門將在清理現場後調查確切事故原因。

本月7日,LG化學在印度投資的一家化工廠發生重大事故,廠內兩座5000公噸級苯乙烯儲存槽因不明原因發生嚴重外洩,造成12人死亡,1000多人住院。

【其他文章推薦】

※各種攤販小吃設備,沙威瑪機應有盡有!

※家庭、朋友聚會,享受輕鬆烤肉必備外燴烤爐NO.1

※十大封口機人氣排行榜-烘焙必備幫手!

※如何知道自已的電腦cpu支不支持AVX指令集?

※臭氧機推薦

※專業客製化禮物、贈品設計,辦公用品常見【L夾】搖身一變大受好評!!

0基礎學算法 第七彈 位運算

  今天要講的內容主要分為兩個大塊,一個呢就是位運算,另一個呢就是關於二進制的原碼反碼補碼,位運算的應用也算是比較多的了,進行位運算有時候可以省略不少事情呢,當然,也不是說不會位運算就會天崩地裂,畢竟事實上,在學位運算之前,我都是用其他方法來強制性模擬位運算(當時我並不知道什麼是位運算)不過多學一些也是好的,省事兒,而且更快

  一,二進制的原碼反碼和補碼

  相信二進制沒有人不熟悉的,逢二進一,一個二進制數由0和1組成,他的最高位就是他的正負符號,0是正數,1是負數,例如10001000,這就是一個負的二進制數,當然最高位是0就是正數

  原碼之所以叫做原碼,必然是因為它就像是萬物之源,補碼、反碼都是在他的基礎上變化的,當最高位是0的時候,也就是正數的時候,原碼==反碼==補碼

  以下關於反碼及補碼的概念是基於最高位是1的情況(負數的情況)

  反碼,顧名思義,是原碼顛倒過來的樣子,除了最高位表示負號的那個“1”不變以外,其他的數位上的數  全部取反,1變0,0變1,假設原碼為1001010100,它的反碼就是1110101011,再假設原碼是0111001,它的反碼就是0111001,是不是有點奇怪,哈,我前面說過了,當原碼是正數的時候,原碼==反碼==補碼

  補碼,在原碼是正數的情況下是等於原碼或者反碼的,但是在負數情況下,他恰好是反碼加1,比如原碼是100000,反碼就是111111,補碼就是1000000

  二,進入正題,位運算

  在系統的講位運算之前,先給大家一張圖表,方便大家查詢

  

   註明:位運算符號在c++中用法同算數運算符

  首先是&

  其實位運算不複雜,理解了就好,比如說&,把他理解成1代表true,2代表false,1&&2,明顯為false(2),2&&2明顯也為false(2),只有1&&1的時候才為true(1);

  但是這是if語句裏面的判斷方法,而‘&’的運算方法是這樣的,比如1010&0011,運算過程如下圖

  過程如右圖

  至於|的話。。。就是上下兩個數只要有一個為1,結果就為1啦,如1100|0011=1111

  ‘~’!也比較好理解,做它的運算的時候,只需要傳一個二進制數,針對每一位進行運算可以從在每一位上如~1001=0110;

  ^,亦或,算起來也比較簡單,只要兩位不相等,結果就為1,舉例,1001^0101=1100

我覺得有圖就不用我多講了吧/狗頭/滑稽

   最後一組”<<“和”>>”這個是左移和右移的操作

  請看

  int類型的二進制是32位的,而long long是64位,左移和右移

  例如,給出一個標準的32的int類型00000000000000000000000000000001,當我們使用左移的時候,整體忘左移,並在後面補零

右移也相似,只不過是低位被擠掉了,高位補0

 

 

  有一道題,可以通過位運算做的很簡便,但如果你不會位運算的話。。。就另當別論了

  如果你不用位運算,你要寫很複雜的代碼,但是用了,你就只要10行代碼,如假包換(雖然我絕對不換)

  題目鏈接→https://www.luogu.com.cn/problem/P1100

   看到題目,很清晰了,首先錄入兩個字符串,然後將它轉成二進制,接着把前16位和后16位的數交換位置,最後結果轉成十進制輸出!好,完美。。。

  停!

 今天利用我們學的位運算壓根不用這麼麻煩!

  直接用上我們的左移”<<“和右移”>>”啊!

  只要左移后16位,右移前16位不就好了?

  廢話不多說,大家請看最短代碼!

#include<bits/stdc++.h>
using namespace std;
unsigned int x;
int main(){
    cin>>x;//錄入x
    cout<<((x<<16)|(x>>16));//將左移16位后的x和右移16位的x用或"|"將他們重新連起來
    return 0;//完美
}

  完美撒花

  如果覺得我講的不錯的,麻煩點個關注,點個贊,以後還會持續更新的

ps:歡迎大家進群 群號:1031457671

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

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

C#預處理器指令 -0016

預處理指令

這些指令/命令不會轉換為可執行代碼,但會影響編譯過程的各個方面;列如,可以讓編譯器不編譯某一部分代碼等。

C#中主要的預處理指令

#define和#undef

#define指令定義:

#define DEBUG

它告訴編譯器存在DEBUG這個符號;這個符號不是實際代碼的一部分,而只是在編譯器編譯代碼時候可能會根據這個符號做條件編譯。

#undef定義:

#undef DEBUG

用來移除定義的符號DEBUG。如果不存在這樣的標記,#undef指令則不會生效。同樣,用#define再次定義一個同名的標記也不會有任何變化。

注意:

  1. 你需要將#define和#undef指令寫在實際業務代碼開始之前的位置。
  2. #define本身沒有什麼用,需要和其他預處理器指令結合使用;比如 #if

#if, #elif, #else和#endif

這些指令告訴編譯器是否要編譯包含在其中的代碼塊。例如:

int DoSomeWork(double x)
{
	// do something
	#if DEBUG
		Console.WriteLine($"x is {x}");
	#endif
}

這段代碼中的Console.Writeline語句,只有在前面用#define指令定義了符號DEBUG后才會在編譯的時候,真正被編譯到;

如果編譯器沒發現DEBUG符號,就會在編譯的時候忽略這句代碼。 

#elif(= else if)和#else指令可以用在#if塊中:

#define ENTERPRISE
#define W10
// further on in the file
#if ENTERPRISE
// do something
	#if W10
	// some code that is only relevant to enterprise
	// edition running on W10
	#endif
#elif PROFESSIONAL
// do something else
#else
// code for the leaner version
#endif

#if和#elif還支持有限的一些邏輯操作符,你可以用使用!,==,!=和||等。

一個標記如果存在,則認為是true,如果沒有定義,就認為是false,因此你也可以這樣使用:

#if W10 && (ENTERPRISE==false) // if W10 is defined but ENTERPRISE isn't

 

#warning和#error

 當編譯器遇到#warning的時候,會產生警告信息;

當編譯器遇到#error的時候,會產生錯誤信息;

    class Program
    {
        static void Main(string[] args)
        {

#warning this is a warning message which will be shown when compile

            Console.WriteLine("Hello World!");

#error this is a error message, and will break build
        }
    }

  編譯結果:

Program.cs(10,10): warning CS1030: #warning: 'this is a warning message which will be shown when compile' [/define_warning/define_warning.csproj]
Program.cs(14,8): error CS1029: #error: 'this is a error message, and will break build' [/define_warning/define_warning.csproj]
    1 Warning(s)
    1 Error(s)

  使用這些指令可以檢查#define語句是不是做錯了什麼事,使用#warning可以提醒要做些事情:

#if DEBUG && RELEASE
#error "You've defined DEBUG and RELEASE simultaneously!"
#endif
#warning "Don't forget to remove this line before the boss tests the code!"
Console.WriteLine("*I love this job.*");

  

#region和#endregion

 可以用來標識一段代碼,在Visual Studio或其他能夠識別的IDE里比較有用。

#region Member Field Declarations
int x;
double d;
Currency balance;
#endregion

  

#line

 #line指令可以用來改變編譯器輸出警告和錯誤時相應的文件名和行號信息。這個實際中,用的可能會比較少。

主要是在用第三方包的時候,有時候會導致編譯器報告的行號或文件名與實際不匹配。

#line可以用於還原這種匹配。

#line 164 "Core.cs" // We happen to know this is line 164 in the file Core.cs, 
// before the intermediate package mangles it.
// later on
#line default // restores default line numbering

  

#pragma

 #pragma指令可以用來終止或恢復某個指定編號到編譯器警告。

與命令行選項不同,#pragma指令可以在類或方法級別實現。

例如:

    class Program
    {
        static void Main(string[] args)
        {
            int i = 0;
            Console.WriteLine("Hello World!");
        }
    }

  編譯是會有warning:

Program.cs(9,17): warning CS0219: The variable 'i' is assigned but its value is never used [/define_warning/define_warning.csproj]
    1 Warning(s)
    0 Error(s)  

 從warning信息可以看出是warning CS0219,加入#pragma后就不會有warning了。

#pragma warning disable CS0219
    public class Program
    {
        static void Main(string[] args)
        {
            int i = 0;
            Console.WriteLine("Hello World!");
        }
    }
#pragma warning restore CS0219

 注意:warning的代碼是區分大小寫的,CS2019要大寫,如果寫成cs2019則沒有用。 

 

學習資料

C#高級編程(第11版) C# 7 & .NET Core 2.0(.NET開發經典名著)

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

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

Python流程控制語句詳解

1.程序結構

計算機在解決問題時,分別是順序執行所有語句、選擇執行部分語句、循環執行部分語句,分別是:順序結構、選擇結構、循環結構。如下圖:

2.選擇語句

  2.1最簡單的if語句

  Python使用保留字if來組成選擇語句,其格式如下:

if 表達式:
   代碼塊

  表達式可以是一個單純的布爾值或者變量,也可以是比較表達式或者邏輯表達式,如果表達式值為真,則執行“代碼塊”;如果值為假,就跳過“代碼塊”,執行後面語句,如圖

  

   注:1.在Python中,當表達式的值為非零的數或者非空的字符串時,if語句也認為是條件成立(即為真值)。

··     2.使用if語句時,如果只有一條語句,那麼語句塊可以直接寫到冒號“:”的右側。但為了代碼可讀性不建議這麼做。

     3.常見錯誤: 

1.if語句後面未加冒號
number = 3
if number == 3   #後面未加冒號,正確的是結尾處添加英文半角的冒號:if number == 3:
    print(number)
2.使用if語句時,如果在符合條件時,需要執行多個語句,一定要記得按照邏輯順序進行代碼縮進,否則程序的本意會有變化,但程序不會報錯,且bug不容易發現。
代碼1:
number = 3
if number == 3:
  print(number)
print('這個是5')
代碼2:
number = 3
if number == 3:
  print(number)
  print('這個是5'

  2.2.if  …… else 語句

  Python中提供 if …else 語句來解決兩個選擇問題,其格式如下:

if 表達式:
    語句塊1
else:
    語句塊2

  使用 if …else 語句時,表達式可以是一個單純的布爾值或者變量,也可以是比較表達式或者邏輯表達式,如果表達式值為真,則執行“代碼塊”;如果值為假,執行else後面的代碼塊。如圖所示:

  

  技巧:

if…else額語句可以使用條件表達式進行簡化,如下:
a = 5
if a > 0:
    b = a
else:
    b = -a
print(b)

簡化:
a = 5
b = a if a > 0 else -a
print(b)

  注:1. 在使用else 語句時,else一定不可以單獨使用,它必須和保留字if一起搭配使用。

    2.程序中使用if…else 語句時,如果出現多個if 語句多餘else語句的情況,那麼該else語句將會根據確定該else 語句屬於哪個if語句。

  2.3.if…elif…else語句

  在開發程序時遇到多選一的情況,則可以使用if …elif…else語句,具體情況如下:

if 表達式1:
    語句塊1
elif 表達式2:
    語句塊2
elif 表達式3:
    語句塊3
…
else:
    語句塊n

  使用 if …elif…else 語句時,表達式可以是一個單純的布爾值或者變量,也可以是比較表達式或者邏輯表達式,如果表達式值為真,則執行語句;如果值為假,則跳過該執行語句,進行下一個elif判斷,只有表達式全部為假的情況下,執行else後面的代碼塊。如圖所示:

   注:1. if 和 elif 都需要判斷表達式的真假,而 else 則不需要判斷;另外 elif 和 elif 都需要跟 if 一起使用,不能單獨使用。

     2. 使用if語句時盡量避免遵循以下原則:

(1).當使用布爾類型的變量作為判斷條件時,假設布爾類型變量為flag,較為規範格式;
if flag:   #表示為真
if not flag #表示為假
不符合規範格式:
if flag == True:
if flag == False:
(2).使用 " if 1 == a: " 這樣的書寫格式可以防止錯寫成 " if  a = 1: "這種形式,從而避免出錯 

  2.4 if 語句的嵌套

  前面已經介紹了3種形式的 if 語句,這三種都可以進行相互嵌套:

  (1) . 在最簡單的if語句中嵌套 if……else語句,形式如下:

if 表達式1: if 表達式2: 語句塊1 else: 語句塊2

  (2). 在if……else中嵌套if……else語句,形式如下:

if 表達式1:
    if 表達式2:
        語句塊1
    else:
        語句塊2
else:
    if 表達式3:
        語句塊3
    else:
        語句塊4

  注:if 選擇語句可以有多種嵌套方式,開發時可以可以根據自身的需要進行選擇合適的嵌套方式,但一定要嚴格控制好不同級別代碼的縮進量。

3.條件表達式

  在程序開發過程中,經常會根據表達式的結果,有條件的進行賦值,例如返回最大值:

a = 6
b = 3
if a > b:
    c = a
else:
    c = b

  針對以上代碼,使用條件表達式進行簡化,如下:

a = 6
b = 3
c = a if a > b else b

4.循環語句

  4.1 while 循環

  while循環是通過一個條件來控制是否要繼續反覆執行循環體(循環體是指一組被重複執行的語句)中的語句。

while 條件表達式:
    循環體

  當條件表達式的返回值為真時,則執行循環體中的語句,執行完畢后,重新判斷條件表達式的返回值,直到表達式返回的結果為假是退出循環體。

  

   注:在使用while循環語句時,一定不要忘記添加將循環條件改變為Flase的代碼,否則,將產生死循環。但開發中也離不開死循環,可根據情況進行編寫。

   4.2. for循環

  for 循環是一個依次重複執行的循環,通常適用於枚舉、遍歷序列和對象中的元素。語法如下:

for 迭代變量 in 可迭代對象:
    循環體

  迭代變量用於保存讀除的值,對象為遍歷或迭代的對象,該對象可以是任何有序的序列對象,如字符串,列表,元組等,循環體為一組被重複執行的語句。

  

  for循環語句可以最基本的應用就是進行數值循環和遍歷字符串。還可以進行遍歷列表、元組、集合和字典。

  4.3. 循環嵌套

  在Python中,是允許在一個循環體中嵌套另一個循環。

  (1). 在while循環中嵌套while循環

while 條件表達式1:
    while 條件表達式2:
        循環體2
    循環體1

  (2). 在for 循環中嵌套 for 循環

for 迭代變量1 in 對象1:
    for 迭代變量2 in 對象2:
        循環體2
    循環體1

  (3). 在while 循環中嵌套 for 循環

while 條件表達式:
    for 迭代變量 in 對象:
        循環體2
    循環體1

  (4). 在 for 循環中嵌套 while 循環

for 迭代變量 in 對象:
    while 條件表達式:
        循環體2
    循環體1

  特殊案例:九九乘法表

for i in range(0,10):
    for j in range(1,i+1):
        print(str(j) + "*"  + str(i) + "=" + str(i * j) +"\t" ,end = " "
    print("")

5.跳轉語句

  當循環滿足一定條件時,程序會一直執行下去,如果需要在中間離開循環,也就是for循環結束重複之前,或者while循環找到結束條件之前,即break語句和continue語句。

  5.1 break語句

  break可以終止當前循環,包括for循環和while循環在內的所有控制語句。

  在while中使用break語句:

 

while 條件表達式1:
    執行語句
    if 條件表達式2:
        break

  在for 中使用break語句

for 迭代變量 in 對象:
    if 條件表達式:
        break

  

           while語句使用break                                                             for語句中使用break

  5.2.continue語句

  continue語句的作用沒有break語句強大,他只能終止本次循環而提前進入下次循環中。

  在while中使用continue語句

while 條件表達式1:
    執行代碼
    if 條件表達式2:
        continue

  在for 中使用continue語句

for 迭代變量 in 對象:
    if 條件表達式:
        continue

         

            while語句使用continue                                                     for語句中使用continue

  注:break與continue的區別

    break語句一般會結合if 語句進行搭配使用,表示在某種條件下,跳出循環。如果使用嵌套循環,break語句將跳出最內層循環。

    continue語句一般也會結合if語句進行搭配使用,表示在某種條件下,跳出當前循環的剩下語句,繼續進行下一輪循環,如果使用嵌套循環,continue語句將只跳過最內層循環中剩餘語句。

6.pass語句

  在Python中pass語句表示空語句,它不做任何事情,一般起到站位作用,常用在代碼調試等。

#例:
for i in range(1,10):    #輸出1~10的數不包含10
    if i % 2 == 0:         #判斷是否是偶數   
        print(i,end="")     #在同一行打印偶數 
    else:                      #不是偶數
        pass                    #佔位符,不做任何事情,直接跳過

#輸出結果為:
2 4 6 8

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

【其他文章推薦】

※USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

Alink漫談(七) : 如何劃分訓練數據集和測試數據集

Alink漫談(七) : 如何劃分訓練數據集和測試數據集

目錄

  • Alink漫談(七) : 如何劃分訓練數據集和測試數據集
    • 0x00 摘要
    • 0x01 訓練數據集和測試數據集
    • 0x02 Alink示例代碼
    • 0x03 批處理
      • 3.1 得到記錄數
      • 3.2 隨機選取記錄
        • 3.2.1 得到總記錄數
        • 3.2.2 決定每個task選擇記錄數
        • 3.2.3 每個task選擇記錄
      • 3.3 設置訓練數據集和測試數據集
    • 0x04 流處理
    • 0x05 參考

0x00 摘要

Alink 是阿里巴巴基於實時計算引擎 Flink 研發的新一代機器學習算法平台,是業界首個同時支持批式算法、流式算法的機器學習平台。本文將為大家展現Alink如何劃分訓練數據集和測試數據集。

0x01 訓練數據集和測試數據集

兩分法

一般做預測分析時,會將數據分為兩大部分。一部分是訓練數據,用於構建模型,一部分是測試數據,用於檢驗模型。

三分法

但有時候模型的構建過程中也需要檢驗模型/輔助模型構建,這時會將訓練數據再分為兩個部分:1)訓練數據;2)驗證數據(Validation Data)。所以這種情況下會把數據分為三部分。

  • 訓練數據(Train Data):用於模型構建。
  • 驗證數據(Validation Data):可選,用於輔助模型構建,可以重複使用。
  • 測試數據(Test Data):用於檢測模型構建,此數據只在模型檢驗時使用,用於評估模型的準確率。絕對不允許用於模型構建過程,否則會導致過渡擬合。

Training set是用來訓練模型或確定模型參數的,如ANN中權值等;

Validation set是用來做模型選擇(model selection),即做模型的最終優化及確定,如ANN的結構;

Test set則純粹是為了測試已經訓練好的模型的推廣能力。當然test set並不能保證模型的正確性,他只是說相似的數據用此模型會得出相似的結果。

實際應用

實際應用中,一般只將數據集分成兩類,即training set 和test set,大多數文章並不涉及validation set。我們這裏也不涉及。大家常用的sklearn的train_test_split函數就是將矩陣隨機劃分為訓練子集和測試子集,並返回劃分好的訓練集測試集樣本和訓練集測試集標籤。

0x02 Alink示例代碼

首先我們給出示例代碼,然後會深入剖析:

public class SplitExample {
  public static void main(String[] args) throws Exception {
    String url = "iris.csv";
    String schema = "sepal_length double, sepal_width double, petal_length double, petal_width double, category string";

    //這裡是批處理
    BatchOperator data = new CsvSourceBatchOp().setFilePath(url).setSchemaStr(schema);
    SplitBatchOp spliter = new SplitBatchOp().setFraction(0.8);
    spliter.linkFrom(data);
    BatchOperator trainData = spliter;
    BatchOperator testData = spliter.getSideOutput(0);

    // 這裡是流處理
    CsvSourceStreamOp dataS = new CsvSourceStreamOp().setFilePath(url).setSchemaStr(schema);
    SplitStreamOp spliterS = new SplitStreamOp().setFraction(0.4);
    spliterS.linkFrom(dataS);
    StreamOperator train_data = spliterS;
    StreamOperator test_data = spliterS.getSideOutput(0);
  }
}

0x03 批處理

SplitBatchOp是分割批處理的主要類,具體構建DAG的工作是在其linkFrom完成的。

總體思路比較簡單:

  1. 假定有一個採樣比例 fraction
  2. 將數據集分區,并行計算每個分區上的記錄數
  3. 把每個分區上的記錄數累積,得到所有記錄總數 totCount
  4. 從上而下計算出一個採樣總數:numTarget = totCount * fraction
  5. 因為具體選擇元素是在每個分區上做的,所以在每個分區上,分別計算出來這個分區應該採樣的記錄數,比如第n個分區上應採樣記錄數:task_n_count * fraction
  6. 把這些分區 “應該採樣的記錄數” 累積,得出來從下而上計算出的採樣總數: totSelect = task_1_count * fraction + task_2_count * fraction + ... task_n_count * fraction
  7. numTarget 和 totSelect 可能不相等,所以隨機決定把多出來的 numTarget - totSelect 加入到某一個task中。
  8. 在每個task上採樣得到具體的記錄。

3.1 得到記錄數

如果要分割數據,首先必須知道數據集的記錄數。比如這個DataSet的記錄是1萬個?還是十萬個?因為數據集可能會很大,所以這一步操作也使用了并行處理,即把數據分區,然後通過mapPartition操作得到每一個分區上元素的數目。

DataSet<Tuple2<Integer, Long>> countsPerPartition = DataSetUtils.countElementsPerPartition(rows); //返回哪個task有哪些記錄數

DataSet<long[]> numPickedPerPartition = countsPerPartition
    .mapPartition(new CountInPartition(fraction)) //計算總數
    .setParallelism(1)
    .name("decide_count_of_each_partition");

因為每個分區就對應了一個task,所以我們也可以認為,這是獲取了每個task的記錄數。

具體工作是在 DataSetUtils.countElementsPerPartition 中完成的。返回類型是<index of this subtask, record count in this subtask>,比如3號task擁有30個記錄。

public static <T> DataSet<Tuple2<Integer, Long>> countElementsPerPartition(DataSet<T> input) {
   return input.mapPartition(new RichMapPartitionFunction<T, Tuple2<Integer, Long>>() {
      @Override
      public void mapPartition(Iterable<T> values, Collector<Tuple2<Integer, Long>> out) throws Exception {
         long counter = 0;
         for (T value : values) {
            counter++; //計算本task的記錄總數
         }
         out.collect(new Tuple2<>(getRuntimeContext().getIndexOfThisSubtask(), counter));
      }
   });
}

計算總數的工作其實是在下一階段算子中完成的。

3.2 隨機選取記錄

接下來的工作主要是在 CountInPartition.mapPartition 完成的,其作用是隨機決定每個task選擇多少個記錄。

這時候就不需要并行了,所以 .setParallelism(1)

3.2.1 得到總記錄數

得到了每個分區記錄數之後,我們遍歷每個task的記錄數,然後累積得到總記錄數 totCount(就是從上而下計算出來的總數)。

public void mapPartition(Iterable<Tuple2<Integer, Long>> values, Collector<long[]> out) throws Exception {
	long totCount = 0L;
	List<Tuple2<Integer, Long>> buffer = new ArrayList<>();
	for (Tuple2<Integer, Long> value : values) { //遍歷輸入的所有分區記錄
    totCount += value.f1; //f1是Long類型的記錄數
    buffer.add(value);
	}
  ...
  //後續代碼在下面分析。  
}

3.2.2 決定每個task選擇記錄數

然後CountInPartition.mapPartition函數中會隨機決定每個task會選擇的記錄數。mapPartition的參數 Iterable<Tuple2<Integer, Long>> values 就是前一階段的結果 :一個元祖<task id, 每個task的記錄數目>。

把這些元祖結合在一起,記錄在buffer這個列表中。

buffer = {ArrayList@8972}  size = 4
 0 = {Tuple2@8975} "(3,38)" // 3號task,其對應的partition記錄數是38個。
 1 = {Tuple2@8976} "(2,0)"
 2 = {Tuple2@8977} "(0,38)"
 3 = {Tuple2@8978} "(1,74)"

系統的task數目就是buffer大小。

int npart = buffer.size(); // num tasks

然後,根據”記錄總數“計算出來 “隨機訓練數據的個數numTarget”。比如總數1萬,應該隨機分配20%,於是numTarget就應該是2千。這個数字以後會用到。

long numTarget = Math.round((totCount * fraction));

得到每個task的記錄數目,比如是上面buffer中的 38,0,38,還是74,記錄在 eachCount 中。

for (Tuple2<Integer, Long> value : buffer) {
    eachCount[value.f0] = value.f1;
}

得到每個task中隨機選中的訓練記錄數,記錄在 eachSelect 中。就是每個task目前 “記錄数字 * fraction”。比如3號task記錄數是38個,應該選20%,則38*20%=8個。

然後把這些task自己的“隨機訓練記錄數”再累加起來得到 totSelect(就是從下而上計算出來的總數)。

long totSelect = 0L;
for (int i = 0; i < npart; i++) {
    eachSelect[i] = Math.round(Math.floor(eachCount[i] * fraction));
    totSelect += eachSelect[i];
}

請注意,這時候 totSelect 和 之前計算的numTarget就有具體細微出入了,就是理論上的一個数字,但是我們 從上而下 計算 和 從下而上 計算,其結果可能不一樣。通過下面我們可以看出來。

numTarget = all count * fraction

totSelect = task_1_count * fraction + task_2_count * fraction + ...

所以我們下一步要處理這個細微出入,就得到remain,這是”總體算出來的隨機數目” numTarget 和 “從所有task選中的隨機訓練記錄數累積” totSelect 的差。

if (totSelect < numTarget) {
    long remain = numTarget - totSelect;
    remain = Math.min(remain, totCount - totSelect);

如果剛好個數相等,則就正常分配。

if (remain == totCount - totSelect) {

如果數目不等,隨機決定把”多出來的remain”加入到eachSelect數組中的隨便一個記錄上。

for (int i = 0; i < Math.min(remain, npart); i++) {
    int taskId = shuffle.get(i);
    while (eachSelect[taskId] >= eachCount[taskId]) {
          taskId = (taskId + 1) % npart;
    }
    eachSelect[taskId]++;
}

最後給出所有信息

long[] statistics = new long[npart * 2];
for (int i = 0; i < npart; i++) {
    statistics[i] = eachCount[i];
    statistics[i + npart] = eachSelect[i];
}
out.collect(statistics);

// 我們這裡是4核,所以前面四項是eachCount,後面是eachSelect
statistics = {long[8]@9003} 
 0 = 38 //eachCount
 1 = 38
 2 = 36
 3 = 38
   
 4 = 31 //eachSelect
 5 = 31
 6 = 28
 7 = 30

這些信息是作為廣播變量存儲起來的,馬上下面就會用到。

 .withBroadcastSet(numPickedPerPartition, "counts")

3.2.3 每個task選擇記錄

CountInPartition.PickInPartition函數中會隨機在每個task選擇記錄。

首先得到task數目 和 之前存儲的廣播變量(就是之前剛剛存儲的)。

int npart = getRuntimeContext().getNumberOfParallelSubtasks();
List<long[]> bc = getRuntimeContext().getBroadcastVariable("counts");

分離count和select。

long[] eachCount = Arrays.copyOfRange(bc.get(0), 0, npart);
long[] eachSelect = Arrays.copyOfRange(bc.get(0), npart, npart * 2);

得到總task數目

int taskId = getRuntimeContext().getIndexOfThisSubtask();

得到自己 task 對應的 count, select

long count = eachCount[taskId];
long select = eachSelect[taskId];

添加本task對應的記錄,隨機洗牌打亂順序

for (int i = 0; i < count; i++) {
     shuffle.add(i); //就是把count內的数字加到數組
}
Collections.shuffle(shuffle, new Random(taskId)); //洗牌打亂順序

// suffle舉例
shuffle = {ArrayList@8987}  size = 38
 0 = {Integer@8994} 17
 1 = {Integer@8995} 8
 2 = {Integer@8996} 33
 3 = {Integer@8997} 34
 4 = {Integer@8998} 20
 5 = {Integer@8999} 0
 6 = {Integer@9000} 26
 7 = {Integer@9001} 27
 8 = {Integer@9002} 23
 9 = {Integer@9003} 28
 10 = {Integer@9004} 9
 11 = {Integer@9005} 16
 12 = {Integer@9006} 13
 13 = {Integer@9007} 2
 14 = {Integer@9008} 5
 15 = {Integer@9009} 31
 16 = {Integer@9010} 15
 17 = {Integer@9011} 22
 18 = {Integer@9012} 18
 19 = {Integer@9013} 35
 20 = {Integer@9014} 36
 21 = {Integer@9015} 12
 22 = {Integer@9016} 7
 23 = {Integer@9017} 21
 24 = {Integer@9018} 14
 25 = {Integer@9019} 1
 26 = {Integer@9020} 10
 27 = {Integer@9021} 30
 28 = {Integer@9022} 29
 29 = {Integer@9023} 19
 30 = {Integer@9024} 25
 31 = {Integer@9025} 32
 32 = {Integer@9026} 37
 33 = {Integer@9027} 4
 34 = {Integer@9028} 11
 35 = {Integer@9029} 6
 36 = {Integer@9030} 3
 37 = {Integer@9031} 24

隨機選擇,把選擇后的再排序回來

for (int i = 0; i < select; i++) {
    selected[i] = shuffle.get(i); //這時候select看起來是按照順序選擇,但是實際上suffle裏面已經是亂序
}
Arrays.sort(selected); //這次再排序

// selected舉例,一共30個
selected = {int[30]@8991} 
 0 = 0
 1 = 1
 2 = 2
 3 = 5
 4 = 7
 5 = 8
 6 = 9
 7 = 10
 8 = 12
 9 = 13
 10 = 14
 11 = 15
 12 = 16
 13 = 17
 14 = 18
 15 = 19
 16 = 20
 17 = 21
 18 = 22
 19 = 23
 20 = 26
 21 = 27
 22 = 28
 23 = 29
 24 = 30
 25 = 31
 26 = 33
 27 = 34
 28 = 35
 29 = 36

發送選擇的數據

if (numEmits < selected.length && iRow == selected[numEmits]) {
    out.collect(row);
    numEmits++;
}

3.3 設置訓練數據集和測試數據集

output是訓練數據集,SideOutput是測試數據集。因為這兩個數據集在Alink內部都是Table類型,所以直接使用了SQL算子 minusAll 來完成分割。

this.setOutput(out, in.getSchema());
this.setSideOutputTables(new Table[]{in.getOutputTable().minusAll(this.getOutputTable())});

0x04 流處理

訓練是在SplitStreamOp類完成的,其通過linkFrom完成了模型的構建。

流處理依賴SplitStream 和 SelectTransformation 這兩個類來完成分割流。具體並沒有建立一個物理操作,而只是影響了上游算子如何與下游算子聯繫,如何選擇記錄。

SplitStream <Row> splited = in.getDataStream().split(new RandomSelectorOp(getFraction()));

首先,用RandomSelectorOp來隨機決定輸出時候選擇哪個流。我們可以看到,這裏就是隨便起了”a”, “b” 這兩個名字而已。

class RandomSelectorOp implements OutputSelector <Row> {
   private double fraction;
   private Random random = null;
   @Override
   public Iterable <String> select(Row value) {
      if (null == random) {
         random = new Random(System.currentTimeMillis());
      }
      List <String> output = new ArrayList <String>(1);
      output.add((random.nextDouble() < fraction ? "a" : "b")); //隨機選取数字分配,隨意起的名字
      return output;
   }
}

其次,得到那兩個隨機生成的流。

DataStream <Row> partA = splited.select("a");
DataStream <Row> partB = splited.select("b");

最後把這兩個流分別設置為output和sideOutput。

this.setOutput(partA, in.getSchema()); //訓練集
this.setSideOutputTables(new Table[]{
DataStreamConversionUtil.toTable(getMLEnvironmentId(), partB, in.getSchema())}); //驗證集

最後返回本身,這時候SplitStreamOp擁有兩個成員變量:

this.output就是訓練集。

this.sideOutPut就是驗證集。

return this;

0x05 參考

訓練數據,驗證數據和測試數據分析

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

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案